From 5042e9e46ae522496da297937910d8597aa4850b Mon Sep 17 00:00:00 2001 From: jinmei Date: Sun, 5 Jan 2003 17:12:12 +0000 Subject: [PATCH] upgraded base spec of DHCPv6 and dhcpv6-pd according to the latest drafts. backward compatibility was (basically) not provided. --- kame/kame/dhcp6/Makefile.in | 7 +- kame/kame/dhcp6/cfparse.y | 220 ++++++--- kame/kame/dhcp6/cftoken.l | 16 +- kame/kame/dhcp6/common.c | 502 +++++++++++++++++--- kame/kame/dhcp6/common.h | 15 +- kame/kame/dhcp6/config.c | 392 +++++++++++----- kame/kame/dhcp6/config.h | 66 +-- kame/kame/dhcp6/configure | 26 +- kame/kame/dhcp6/configure.in | 16 +- kame/kame/dhcp6/dhcp6.h | 100 ++-- kame/kame/dhcp6/dhcp6c.c | 193 +++++--- kame/kame/dhcp6/dhcp6c_ia.c | 475 +++++++++++++++++++ kame/kame/dhcp6/dhcp6c_ia.h | 53 +++ kame/kame/dhcp6/dhcp6s.c | 883 +++++++++++++++++++++++++---------- kame/kame/dhcp6/prefixconf.c | 578 +++++++++++------------ kame/kame/dhcp6/prefixconf.h | 23 +- 16 files changed, 2619 insertions(+), 946 deletions(-) create mode 100644 kame/kame/dhcp6/dhcp6c_ia.c create mode 100644 kame/kame/dhcp6/dhcp6c_ia.h diff --git a/kame/kame/dhcp6/Makefile.in b/kame/kame/dhcp6/Makefile.in index f3f0bfc0c6..d17b698e24 100644 --- a/kame/kame/dhcp6/Makefile.in +++ b/kame/kame/dhcp6/Makefile.in @@ -1,5 +1,5 @@ # -# $Id: Makefile.in,v 1.17 2002/12/29 01:08:05 jinmei Exp $ +# $Id: Makefile.in,v 1.18 2003/01/05 17:12:12 jinmei Exp $ # srcdir= @srcdir@ @@ -24,10 +24,11 @@ CFLAGS+= -DCONF_DH6OPT_DNS=@dhcpopt_dns@ \ -DCONF_DH6OPT_PREFIX_INFORMATION=@dhcpopt_pinfo@ \ -DCONF_DH6OPT_PREFIX_REQUEST=@dhcpopt_preq@ \ -DCONF_DH6OPT_IA_PD=@dhcpopt_ia_pd@ \ - -DCONF_DH6OPT_IA_PD_PREFIX=@dhcpopt_ia_pd_prefix@ + -DCONF_DH6OPT_IA_PD_PREFIX=@dhcpopt_ia_pd_prefix@ \ + -DCONF_DH6OPT_STCODE_NOPREFIXAVAIL=@dhcpstcode_no_prefix@ GENSRCS=cfparse.c cftoken.c -CLIENTOBJS= dhcp6c.o common.o config.o prefixconf.o timer.o \ +CLIENTOBJS= dhcp6c.o common.o config.o prefixconf.o dhcp6c_ia.o timer.o \ $(GENSRCS:%.c=%.o) SERVOBJS= dhcp6s.o common.o config.o timer.o $(GENSRCS:%.c=%.o) RELAYOBJS = dhcp6relay.o common.o diff --git a/kame/kame/dhcp6/cfparse.y b/kame/kame/dhcp6/cfparse.y index e6452bfbe0..1c1581f010 100644 --- a/kame/kame/dhcp6/cfparse.y +++ b/kame/kame/dhcp6/cfparse.y @@ -1,4 +1,4 @@ -/* $KAME: cfparse.y,v 1.16 2002/09/24 14:20:49 itojun Exp $ */ +/* $KAME: cfparse.y,v 1.17 2003/01/05 17:12:12 jinmei Exp $ */ /* * Copyright (C) 2002 WIDE Project. @@ -37,6 +37,8 @@ #include +#include + #include "dhcp6.h" #include "config.h" #include "common.h" @@ -77,7 +79,8 @@ extern void yyerror __P((char *, ...)) l->list = (pl); \ } while (0) -static struct cf_namelist *iflist_head, *piflist_head, *hostlist_head; +static struct cf_namelist *iflist_head, *piflist_head, *hostlist_head, + *iapdlist_head; struct cf_list *cf_dns_list; extern int yylex __P((void)); @@ -88,9 +91,10 @@ static void cleanup_cflist __P((struct cf_list *)); %token INTERFACE IFNAME %token PREFIX_INTERFACE SLA_ID SLA_LEN DUID_ID +%token ID_ASSOC IA_PD IAID %token REQUEST SEND ALLOW PREFERENCE %token HOST HOSTNAME DUID -%token OPTION RAPID_COMMIT PREFIX_DELEGATION DNS_SERVERS +%token OPTION RAPID_COMMIT PREFIX_DELEGATION IA_PD DNS_SERVERS %token INFO_ONLY %token NUMBER SLASH EOS BCL ECL STRING PREFIX INFINITY %token COMMA @@ -102,10 +106,11 @@ static void cleanup_cflist __P((struct cf_list *)); struct dhcp6_prefix *prefix; } -%type IFNAME HOSTNAME DUID_ID STRING +%type IFNAME HOSTNAME DUID_ID STRING IAID %type NUMBER duration %type declaration declarations dhcpoption ifparam ifparams %type address_list address_list_ent +%type iaconf_list iaconf prefix_interface %type prefixparam %% @@ -116,9 +121,9 @@ statements: statement: interface_statement - | prefix_interface_statement | host_statement | option_statement + | ia_statement ; interface_statement: @@ -133,18 +138,6 @@ interface_statement: } ; -prefix_interface_statement: - PREFIX_INTERFACE IFNAME BCL ifparams ECL EOS - { - struct cf_namelist *ifl; - - MAKE_NAMELIST(ifl, $2, $4); - - if (add_namelist(ifl, &piflist_head)) - return (-1); - } - ; - host_statement: HOST HOSTNAME BCL declarations ECL EOS { @@ -169,6 +162,32 @@ option_statement: } ; +ia_statement: + ID_ASSOC IA_PD IAID BCL iaconf_list ECL EOS + { + struct cf_namelist *iapd; + + MAKE_NAMELIST(iapd, $3, $5); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + | ID_ASSOC IA_PD BCL iaconf_list ECL EOS + { + struct cf_namelist *iapd; + char *zero; + + if ((zero = strdup("0")) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + MAKE_NAMELIST(iapd, zero, $4); + + if (add_namelist(iapd, &iapdlist_head)) + return (-1); + } + ; + address_list: { $$ = NULL; } | address_list address_list_ent @@ -306,6 +325,14 @@ dhcpoption: /* currently no value */ $$ = l; } + | IA_PD NUMBER + { + struct cf_list *l; + + MAKE_CFLIST(l, DHCPOPT_IA_PD, NULL, NULL); + l->num = $2; + $$ = l; + } | DNS_SERVERS { struct cf_list *l; @@ -316,6 +343,110 @@ dhcpoption: } ; +prefixparam: + STRING SLASH NUMBER duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = $3; + if ($4 < 0) + pconf0.pltime = DHCP6_DURATITION_INFINITE; + else + pconf0.pltime = (u_int32_t)$4; + pconf0.vltime = pconf0.pltime; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + | STRING SLASH NUMBER duration duration + { + struct dhcp6_prefix pconf0, *pconf; + + memset(&pconf0, 0, sizeof(pconf0)); + if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { + yywarn("invalid IPv6 address: %s", $1); + free($1); + return (-1); + } + free($1); + /* validate other parameters later */ + pconf0.plen = $3; + if ($4 < 0) + pconf0.pltime = DHCP6_DURATITION_INFINITE; + else + pconf0.pltime = (u_int32_t)$4; + if ($5 < 0) + pconf0.vltime = DHCP6_DURATITION_INFINITE; + else + pconf0.vltime = (u_int32_t)$5; + + if ((pconf = malloc(sizeof(*pconf))) == NULL) { + yywarn("can't allocate memory"); + return (-1); + } + *pconf = pconf0; + + $$ = pconf; + } + + +duration: + INFINITY + { + $$ = -1; + } + | NUMBER + { + $$ = $1; + } + ; + +iaconf_list: + { $$ = NULL; } + | iaconf_list iaconf + { + struct cf_list *head; + + if ((head = $1) == NULL) { + $2->next = NULL; + $2->tail = $2; + head = $2; + } else { + head->tail->next = $2; + head->tail = $2; + } + + $$ = head; + } + ; + +iaconf: + prefix_interface { $$ = $1; } + ; + +prefix_interface: + PREFIX_INTERFACE IFNAME BCL ifparams ECL EOS + { + struct cf_list *ifl; + + MAKE_CFLIST(ifl, IACONF_PIF, $2, $4); + $$ = ifl; + } + ; + ifparams: { $$ = NULL; } | ifparams ifparam @@ -354,57 +485,18 @@ ifparam: } ; -prefixparam: - STRING SLASH NUMBER duration - { - struct dhcp6_prefix pconf0, *pconf; - - memset(&pconf0, 0, sizeof(pconf0)); - if (inet_pton(AF_INET6, $1, &pconf0.addr) != 1) { - yywarn("invalid IPv6 address: %s", $1); - free($1); - return (-1); - } - free($1); - /* validate other parameters later */ - pconf0.plen = $3; - if ($4 < 0) - pconf0.duration = DHCP6_DURATITION_INFINITE; - else - pconf0.duration = (u_int32_t)$4; - - if ((pconf = malloc(sizeof(*pconf))) == NULL) { - yywarn("can't allocate memory"); - return (-1); - } - *pconf = pconf0; - - $$ = pconf; - } - -duration: - INFINITY - { - $$ = -1; - } - | NUMBER - { - $$ = $1; - } - ; - %% /* supplement routines for configuration */ static int add_namelist(new, headp) struct cf_namelist *new, **headp; { - struct cf_namelist *ifp; - + struct cf_namelist *n; + /* check for duplicated configuration */ - for (ifp = *headp; ifp; ifp = ifp->next) { - if (strcmp(ifp->name, new->name) == 0) { - yywarn("duplicated interface: %s (ignored)", + for (n = *headp; n; n = n->next) { + if (strcmp(n->name, new->name) == 0) { + yywarn("duplicated name: %s (ignored)", new->name); cleanup_namelist(new); return (0); @@ -422,8 +514,8 @@ static void cleanup() { cleanup_namelist(iflist_head); - cleanup_namelist(piflist_head); cleanup_namelist(hostlist_head); + cleanup_namelist(iapdlist_head); cleanup_cflist(cf_dns_list); } @@ -467,10 +559,10 @@ cleanup_cflist(p) int cf_post_config() { - if (configure_interface(iflist_head)) + if (configure_ia(iapdlist_head, IATYPE_PD)) config_fail(); - if (configure_prefix_interface(piflist_head)) + if (configure_interface(iflist_head)) config_fail(); if (configure_host(hostlist_head)) diff --git a/kame/kame/dhcp6/cftoken.l b/kame/kame/dhcp6/cftoken.l index 4282da023e..95e10acffd 100644 --- a/kame/kame/dhcp6/cftoken.l +++ b/kame/kame/dhcp6/cftoken.l @@ -1,4 +1,4 @@ -/* $KAME: cftoken.l,v 1.15 2002/09/24 14:20:49 itojun Exp $ */ +/* $KAME: cftoken.l,v 1.16 2003/01/05 17:12:12 jinmei Exp $ */ %{ /* @@ -93,6 +93,7 @@ ecl \} %s S_PREF %s S_HOST %s S_DUID +%s S_IA %% %{ @@ -142,6 +143,7 @@ ecl \} rapid-commit { DECHO; return (RAPID_COMMIT); } prefix-delegation { DECHO; return (PREFIX_DELEGATION); } +ia-pd { DECHO; return (IA_PD); } domain-name-servers { DECHO; return (DNS_SERVERS); } /* generic options */ @@ -149,7 +151,17 @@ ecl \} allow { DECHO; return (ALLOW); } - /* interface parameters for delegated prefix configuration */ + /* identity association */ +id-assoc { DECHO; BEGIN S_IA; return(ID_ASSOC); } +pd { DECHO; return(IA_PD); } +{number} { DECHO; yylval.str = strdup(yytext); return(IAID); } +{bcl} { DP("begin of closure"); BEGIN S_CNF; return (BCL); } + + /* + * interface parameters for delegated prefix configuration. + * when lex reads an interface name, the state will be back to + * S_CNF. + */ prefix-interface { DECHO; BEGIN S_IFACE; return (PREFIX_INTERFACE); } sla-id { DECHO; return (SLA_ID); } sla-len { DECHO; return (SLA_LEN); } diff --git a/kame/kame/dhcp6/common.c b/kame/kame/dhcp6/common.c index d767094cfd..e2eb4424f5 100644 --- a/kame/kame/dhcp6/common.c +++ b/kame/kame/dhcp6/common.c @@ -1,4 +1,4 @@ -/* $KAME: common.c,v 1.68 2002/12/29 00:54:48 jinmei Exp $ */ +/* $KAME: common.c,v 1.69 2003/01/05 17:12:12 jinmei Exp $ */ /* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. @@ -82,7 +82,11 @@ int debug_thresh; #if 0 static unsigned int if_maxindex __P((void)); #endif +static int dhcp6_count_list __P((struct dhcp6_list *)); static int in6_matchflags __P((struct sockaddr *, char *, int)); +static int copyout_option __P((char *, char *, struct dhcp6_listval *)); +static int copyin_option __P((int, struct dhcp6opt *, struct dhcp6opt *, + struct dhcp6_list *)); static ssize_t gethwid __P((char *, int, const char *, u_int16_t *)); static int get_delegated_prefixes __P((char *, char *, struct dhcp6_optinfo *)); @@ -91,16 +95,12 @@ int dhcp6_copy_list(dst, src) struct dhcp6_list *dst, *src; { - struct dhcp6_listval *ent, *dent; + struct dhcp6_listval *ent; for (ent = TAILQ_FIRST(src); ent; ent = TAILQ_NEXT(ent, link)) { - if ((dent = malloc(sizeof(*dent))) == NULL) + if (dhcp6_add_listval(dst, ent->type, + &ent->uv, &ent->sublist) == NULL) goto fail; - - memset(dent, 0, sizeof(*dent)); - memcpy(&dent->uv, &ent->uv, sizeof(ent->uv)); - - TAILQ_INSERT_TAIL(dst, dent, link); } return (0); @@ -118,13 +118,13 @@ dhcp6_clear_list(head) while ((v = TAILQ_FIRST(head)) != NULL) { TAILQ_REMOVE(head, v, link); - free(v); + dhcp6_clear_listval(v); } return; } -int +static int dhcp6_count_list(head) struct dhcp6_list *head; { @@ -137,20 +137,40 @@ dhcp6_count_list(head) return (i); } +void +dhcp6_clear_listval(lv) + struct dhcp6_listval *lv; +{ + dhcp6_clear_list(&lv->sublist); + free(lv); +} + +/* + * Note: this function only searches for the first entry that matches + * VAL. It also does not care about sublists. + */ struct dhcp6_listval * -dhcp6_find_listval(head, val, type) +dhcp6_find_listval(head, type, val, option) struct dhcp6_list *head; - void *val; dhcp6_listval_type_t type; + void *val; + int option; { struct dhcp6_listval *lv; for (lv = TAILQ_FIRST(head); lv; lv = TAILQ_NEXT(lv, link)) { + if (lv->type != type) + continue; + switch(type) { case DHCP6_LISTVAL_NUM: if (lv->val_num == *(int *)val) return (lv); break; + case DHCP6_LISTVAL_STCODE: + if (lv->val_num16 == *(u_int16_t *)val) + return (lv); + break; case DHCP6_LISTVAL_ADDR6: if (IN6_ARE_ADDR_EQUAL(&lv->val_addr6, (struct in6_addr *)val)) { @@ -158,13 +178,23 @@ dhcp6_find_listval(head, val, type) } break; case DHCP6_LISTVAL_PREFIX6: - if (IN6_ARE_ADDR_EQUAL(&lv->val_prefix6.addr, + if ((option & MATCHLIST_PREFIXLEN) && + lv->val_prefix6.plen == + ((struct dhcp6_prefix *)val)->plen) { + return (lv); + } else if (IN6_ARE_ADDR_EQUAL(&lv->val_prefix6.addr, &((struct dhcp6_prefix *)val)->addr) && lv->val_prefix6.plen == ((struct dhcp6_prefix *)val)->plen) { return (lv); } break; + case DHCP6_LISTVAL_IAPD: + if (lv->val_ia.iaid == + ((struct dhcp6_ia *)val)->iaid) { + return (lv); + } + break; } } @@ -172,39 +202,56 @@ dhcp6_find_listval(head, val, type) } struct dhcp6_listval * -dhcp6_add_listval(head, val, type) - struct dhcp6_list *head; - void *val; +dhcp6_add_listval(head, type, val, sublist) + struct dhcp6_list *head, *sublist; dhcp6_listval_type_t type; + void *val; { - struct dhcp6_listval *lv; + struct dhcp6_listval *lv = NULL; if ((lv = malloc(sizeof(*lv))) == NULL) { dprintf(LOG_ERR, "%s" "failed to allocate memory for list " "entry", FNAME); - return (NULL); + goto fail; } memset(lv, 0, sizeof(*lv)); + lv->type = type; + TAILQ_INIT(&lv->sublist); switch(type) { case DHCP6_LISTVAL_NUM: lv->val_num = *(int *)val; break; + case DHCP6_LISTVAL_STCODE: + lv->val_num16 = *(u_int16_t *)val; + break; case DHCP6_LISTVAL_ADDR6: lv->val_addr6 = *(struct in6_addr *)val; break; case DHCP6_LISTVAL_PREFIX6: lv->val_prefix6 = *(struct dhcp6_prefix *)val; break; + case DHCP6_LISTVAL_IAPD: + lv->val_ia = *(struct dhcp6_ia *)val; + break; default: dprintf(LOG_ERR, "%s" "unexpected list value type (%d)", FNAME, type); - exit(1); + goto fail; } + if (sublist && dhcp6_copy_list(&lv->sublist, sublist)) + goto fail; + TAILQ_INSERT_TAIL(head, lv, link); return (lv); + + fail: + if (lv) + free(lv); + + return (NULL); } struct dhcp6_event * @@ -219,6 +266,7 @@ dhcp6_create_event(ifp, state) FNAME); return (NULL); } + memset(ev, 0, sizeof(*ev)); ev->ifp = ifp; ev->state = state; TAILQ_INIT(&ev->data_list); @@ -230,13 +278,18 @@ void dhcp6_remove_event(ev) struct dhcp6_event *ev; { + struct dhcp6_eventdata *evd, *evd_next; + dprintf(LOG_DEBUG, "%s" "removing an event on %s, state=%d", FNAME, ev->ifp->ifname, ev->state); - if (!TAILQ_EMPTY(&ev->data_list)) { - dprintf(LOG_ERR, "%s" "assumption failure: " - "event data list is not empty", FNAME); - exit(1); + for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = evd_next) { + evd_next = TAILQ_NEXT(evd, link); + TAILQ_REMOVE(&ev->data_list, evd, link); + if (evd->destructor) + (*evd->destructor)(evd); + else + free(evd); } duidfree(&ev->serverid); @@ -567,7 +620,7 @@ get_duid(idfile, duid) { FILE *fp = NULL; u_int16_t len = 0, hwtype; - struct dhcp6_duid_type1 *dp; /* we only support the type1 DUID */ + struct dhcp6opt_duid_type1 *dp; /* we only support the type1 DUID */ char tmpbuf[256]; /* DUID should be no more than 256 bytes */ if ((fp = fopen(idfile, "r")) == NULL && errno != ENOENT) @@ -588,7 +641,7 @@ get_duid(idfile, duid) "failed to get a hardware address", FNAME); goto fail; } - len = l + sizeof(struct dhcp6_duid_type1); + len = l + sizeof(struct dhcp6opt_duid_type1); } memset(duid, 0, sizeof(*duid)); @@ -611,12 +664,12 @@ get_duid(idfile, duid) } else { u_int64_t t64; - dp = (struct dhcp6_duid_type1 *)duid->duid_id; - dp->dh6duid1_type = htons(1); /* type 1 */ - dp->dh6duid1_hwtype = htons(hwtype); + dp = (struct dhcp6opt_duid_type1 *)duid->duid_id; + dp->dh6_duid1_type = htons(1); /* type 1 */ + dp->dh6_duid1_hwtype = htons(hwtype); /* time is Jan 1, 2000 (UTC), modulo 2^32 */ t64 = (u_int64_t)(time(NULL) - 946684800); - dp->dh6duid1_time = htonl((u_long)(t64 & 0xffffffff)); + dp->dh6_duid1_time = htonl((u_long)(t64 & 0xffffffff)); memcpy((void *)(dp + 1), tmpbuf, (len - sizeof(*dp))); dprintf(LOG_DEBUG, "%s" "generated a new DUID: %s", FNAME, @@ -715,6 +768,7 @@ dhcp6_init_options(optinfo) optinfo->pref = DH6OPT_PREF_UNDEF; + TAILQ_INIT(&optinfo->iapd_list); TAILQ_INIT(&optinfo->reqopt_list); TAILQ_INIT(&optinfo->stcode_list); TAILQ_INIT(&optinfo->dns_list); @@ -729,6 +783,7 @@ dhcp6_clear_options(optinfo) duidfree(&optinfo->clientID); duidfree(&optinfo->serverID); + dhcp6_clear_list(&optinfo->iapd_list); dhcp6_clear_list(&optinfo->reqopt_list); dhcp6_clear_list(&optinfo->stcode_list); dhcp6_clear_list(&optinfo->dns_list); @@ -747,6 +802,8 @@ dhcp6_copy_options(dst, src) goto fail; dst->rapidcommit = src->rapidcommit; + if (dhcp6_copy_list(&dst->iapd_list, &src->iapd_list)) + goto fail; if (dhcp6_copy_list(&dst->reqopt_list, &src->reqopt_list)) goto fail; if (dhcp6_copy_list(&dst->stcode_list, &src->stcode_list)) @@ -771,9 +828,13 @@ dhcp6_get_options(p, ep, optinfo) struct dhcp6_optinfo *optinfo; { struct dhcp6opt *np, opth; - int i, opt, optlen, reqopts, num; + int i, opt, optlen, reqopts; + u_int16_t num; char *cp, *val; u_int16_t val16; + struct dhcp6opt_ia optia; + struct dhcp6_ia ia; + struct dhcp6_list sublist; for (; p + 1 <= ep; p = np) { struct duid duid0; @@ -835,7 +896,7 @@ dhcp6_get_options(p, ep, optinfo) /* need to check duplication? */ if (dhcp6_add_listval(&optinfo->stcode_list, - &num, DHCP6_LISTVAL_NUM) == NULL) { + DHCP6_LISTVAL_STCODE, &num, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to copy " "status code", FNAME); goto fail; @@ -857,7 +918,7 @@ dhcp6_get_options(p, ep, optinfo) dhcp6optstr(num)); if (dhcp6_find_listval(&optinfo->reqopt_list, - &num, DHCP6_LISTVAL_NUM)) { + DHCP6_LISTVAL_NUM, &num, 0)) { dprintf(LOG_INFO, "%s" "duplicated " "option type (%s)", FNAME, dhcp6optstr(opttype)); @@ -865,7 +926,7 @@ dhcp6_get_options(p, ep, optinfo) } if (dhcp6_add_listval(&optinfo->reqopt_list, - &num, DHCP6_LISTVAL_NUM) == NULL) { + DHCP6_LISTVAL_NUM, &num, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to copy " "requested option", FNAME); goto fail; @@ -889,7 +950,7 @@ dhcp6_get_options(p, ep, optinfo) for (val = cp; val < cp + optlen; val += sizeof(struct in6_addr)) { if (dhcp6_find_listval(&optinfo->dns_list, - &num, DHCP6_LISTVAL_ADDR6)) { + DHCP6_LISTVAL_ADDR6, &num, 0)) { dprintf(LOG_INFO, "%s" "duplicated " "DNS address (%s)", FNAME, in6addr2str((struct in6_addr *)val, @@ -898,13 +959,50 @@ dhcp6_get_options(p, ep, optinfo) } if (dhcp6_add_listval(&optinfo->dns_list, - val, DHCP6_LISTVAL_ADDR6) == NULL) { + DHCP6_LISTVAL_ADDR6, val, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to copy " "DNS address", FNAME); goto fail; } nextdns: } + break; + case DH6OPT_IA_PD: + if (optlen + sizeof(struct dhcp6opt) < + sizeof(optia)) + goto malformed; + memcpy(&optia, p, sizeof(optia)); + ia.iaid = ntohl(optia.dh6_ia_iaid); + ia.t1 = ntohl(optia.dh6_ia_t1); + ia.t2 = ntohl(optia.dh6_ia_t2); + + dprintf(LOG_DEBUG, " IA_PD: ID=%lu, T1=%lu, T2=%lu", + ia.iaid, ia.t1, ia.t2); + + /* duplication check */ + if (dhcp6_find_listval(&optinfo->iapd_list, + DHCP6_LISTVAL_IAPD, &ia, 0)) { + dprintf(LOG_INFO, "%s" "duplicated IA_PD %lu", + FNAME, ia.iaid); + break; /* ignore this IA_PD */ + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)((char *)p + sizeof(optia)), + (struct dhcp6opt *)(cp + optlen), &sublist)) { + goto fail; + } + + /* link this option set */ + if (dhcp6_add_listval(&optinfo->iapd_list, + DHCP6_LISTVAL_IAPD, &ia, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + break; case DH6OPT_PREFIX_DELEGATION: if (get_delegated_prefixes(cp, cp + optlen, optinfo)) @@ -929,6 +1027,149 @@ dhcp6_get_options(p, ep, optinfo) return (-1); } +static int +copyin_option(type, p, ep, list) + int type; + struct dhcp6opt *p, *ep; + struct dhcp6_list *list; +{ + int opt, optlen; + char *cp; + struct dhcp6opt *np, opth; + struct dhcp6opt_stcode opt_stcode; + struct dhcp6opt_ia_pd_prefix opt_iapd_prefix; + struct dhcp6_prefix iapd_prefix; + struct dhcp6_list sublist; + + TAILQ_INIT(&sublist); + + for (; p + 1 <= ep; p = np) { + memcpy(&opth, p, sizeof(opth)); + optlen = ntohs(opth.dh6opt_len); + opt = ntohs(opth.dh6opt_type); + + cp = (char *)(p + 1); + np = (struct dhcp6opt *)(cp + optlen); + + dprintf(LOG_DEBUG, "%s" "get DHCP option %s, len %d", + FNAME, dhcp6optstr(opt), optlen); + + if (np > ep) { + dprintf(LOG_INFO, "%s" "malformed DHCP option", FNAME); + goto fail; + } + + switch (opt) { + case DH6OPT_IA_PD_PREFIX: + /* check option context */ + if (type != DH6OPT_IA_PD) { + dprintf(LOG_INFO, "%s" + "%s is an invalid position for %s", FNAME, + dhcp6optstr(type), dhcp6optstr(opt)); + goto nextoption; /* or discard the message? */ + } + /* check option length */ + if (optlen + sizeof(opth) < sizeof(opt_iapd_prefix)) + goto malformed; + + /* copy and convert option values */ + memcpy(&opt_iapd_prefix, p, sizeof(opt_iapd_prefix)); + if (opt_iapd_prefix.dh6_iapd_prefix_prefix_len > 128) { + dprintf(LOG_INFO, "%s" "invalid prefix length " + "(%d)", FNAME, + opt_iapd_prefix.dh6_iapd_prefix_prefix_len); + goto malformed; + } + iapd_prefix.pltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_preferred_time); + iapd_prefix.vltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_valid_time); + iapd_prefix.plen = + opt_iapd_prefix.dh6_iapd_prefix_prefix_len; + memcpy(&iapd_prefix.addr, + &opt_iapd_prefix.dh6_iapd_prefix_prefix_addr, + sizeof(iapd_prefix.addr)); + /* clear padding bits in the prefix address */ + prefix6_mask(&iapd_prefix.addr, iapd_prefix.plen); + + dprintf(LOG_DEBUG, " IA_PD prefix: " + "%s/%d pltime=%lu vltime=%lu", + in6addr2str(&iapd_prefix.addr, 0), + iapd_prefix.plen, + iapd_prefix.pltime, iapd_prefix.vltime); + + if (dhcp6_find_listval(list, DHCP6_LISTVAL_PREFIX6, + &iapd_prefix, 0)) { + dprintf(LOG_INFO, "%s" "duplicated IA_PD " + "prefix %s/%d", FNAME, + iapd_prefix.pltime, iapd_prefix.vltime); + goto nextoption; + } + + /* take care of sub-options */ + TAILQ_INIT(&sublist); + if (copyin_option(opt, + (struct dhcp6opt *)(char *)p + + sizeof(opt_iapd_prefix), + ep, &sublist)) { + goto fail; + } + + if (dhcp6_add_listval(list, DHCP6_LISTVAL_PREFIX6, + &iapd_prefix, &sublist) == NULL) { + dhcp6_clear_list(&sublist); + goto fail; + } + dhcp6_clear_list(&sublist); + break; + case DH6OPT_STATUS_CODE: + /* check option context */ + if (type != DH6OPT_IA_PD && + type != DH6OPT_IA_PD_PREFIX) { + dprintf(LOG_INFO, "%s" + "%s is an invalid position for %s", FNAME, + dhcp6optstr(type), dhcp6optstr(opt)); + goto nextoption; /* or discard the message? */ + } + /* check option length */ + if (optlen + sizeof(opth) < sizeof(opt_stcode)) + goto malformed; + + /* copy and convert option values */ + memcpy(&opt_stcode, p, sizeof(opt_stcode)); + opt_stcode.dh6_stcode_code = + ntohs(opt_stcode.dh6_stcode_code); + + dprintf(LOG_DEBUG, " status code: %s", + dhcp6_stcodestr(opt_stcode.dh6_stcode_code)); + + /* duplication check */ + if (dhcp6_find_listval(list, DHCP6_LISTVAL_STCODE, + &opt_stcode.dh6_stcode_code, 0)) { + dprintf(LOG_INFO, "%s" + "duplicated status code (%d)", FNAME, + opt_stcode.dh6_stcode_code); + goto nextoption; + } + + /* copy-in the code value */ + if (dhcp6_add_listval(list, DHCP6_LISTVAL_STCODE, + &opt_stcode.dh6_stcode_code, NULL) == NULL) + goto fail; + + break; + } + nextoption: + } + + return (0); + + malformed: + dprintf(LOG_INFO, " malformed DHCP option: type %d", opt); + + fail: + dhcp6_clear_list(&sublist); + return (-1); +} + static int get_delegated_prefixes(p, ep, optinfo) char *p, *ep; @@ -936,7 +1177,7 @@ get_delegated_prefixes(p, ep, optinfo) { char *np, *cp; struct dhcp6opt opth; - struct dhcp6_prefix_info pi; + struct dhcp6opt_prefix_info pi; struct dhcp6_prefix prefix; int optlen, opt; @@ -977,13 +1218,15 @@ get_delegated_prefixes(p, ep, optinfo) memset(&prefix, 0, sizeof(prefix)); prefix.addr = pi.dh6_pi_paddr; prefix.plen = pi.dh6_pi_plen; - prefix.duration = ntohl(pi.dh6_pi_duration); + /* XXX */ + prefix.vltime = ntohl(pi.dh6_pi_duration); + prefix.pltime = ntohl(pi.dh6_pi_duration); - if (prefix.duration != DHCP6_DURATITION_INFINITE) { + if (prefix.vltime != DHCP6_DURATITION_INFINITE) { dprintf(LOG_DEBUG, " prefix information: " - "%s/%d duration %ld", + "%s/%d duration %lu", in6addr2str(&prefix.addr, 0), - prefix.plen, prefix.duration); + prefix.plen, prefix.vltime); } else { dprintf(LOG_DEBUG, " prefix information: " "%s/%d duration infinity", @@ -992,7 +1235,7 @@ get_delegated_prefixes(p, ep, optinfo) } if (dhcp6_find_listval(&optinfo->prefix_list, - &prefix, DHCP6_LISTVAL_PREFIX6)) { + DHCP6_LISTVAL_PREFIX6, &prefix, 0)) { dprintf(LOG_INFO, "%s" "duplicated " "prefix (%s/%d)", FNAME, in6addr2str(&prefix.addr, 0), @@ -1000,8 +1243,8 @@ get_delegated_prefixes(p, ep, optinfo) goto nextoption; } - if (dhcp6_add_listval(&optinfo->prefix_list, &prefix, - DHCP6_LISTVAL_PREFIX6) == NULL) { + if (dhcp6_add_listval(&optinfo->prefix_list, + DHCP6_LISTVAL_PREFIX6, &prefix, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to copy a " "prefix", FNAME); goto fail; @@ -1042,7 +1285,7 @@ dhcp6_set_options(bp, ep, optinfo) struct dhcp6_optinfo *optinfo; { struct dhcp6opt *p = bp, opth; - struct dhcp6_listval *stcode; + struct dhcp6_listval *stcode, *op; int len = 0, optlen; char *tmpbuf = NULL; @@ -1115,14 +1358,44 @@ dhcp6_set_options(bp, ep, optinfo) free(tmpbuf); } + for (op = TAILQ_FIRST(&optinfo->iapd_list); op; + op = TAILQ_NEXT(op, link)) { + int optlen; + + tmpbuf = NULL; + if ((optlen = copyout_option(NULL, NULL, op)) < 0) { + dprintf(LOG_INFO, "%s" "failed to count option length", + FNAME); + goto fail; + } + if ((void *)ep - (void *)p < optlen) { + dprintf(LOG_INFO, "%s" "short buffer", FNAME); + goto fail; + } + if ((tmpbuf = malloc(optlen)) == NULL) { + dprintf(LOG_NOTICE, "%s" + "memory allocation failed for DNS options", FNAME); + goto fail; + } + if (copyout_option(tmpbuf, tmpbuf + optlen, op) < 0) { + dprintf(LOG_ERR, "%s" + "failed to construct an option", FNAME); + goto fail; + } + memcpy(p, tmpbuf, optlen); + free(tmpbuf); + p = (struct dhcp6opt *)((char *)p + optlen); + len += optlen; + } + if (!TAILQ_EMPTY(&optinfo->prefix_list)) { char *tp; struct dhcp6_listval *dp; - struct dhcp6_prefix_info pi; + struct dhcp6opt_prefix_info pi; tmpbuf = NULL; optlen = dhcp6_count_list(&optinfo->prefix_list) * - sizeof(struct dhcp6_prefix_info); + sizeof(struct dhcp6opt_prefix_info); if ((tmpbuf = malloc(optlen)) == NULL) { dprintf(LOG_ERR, "%s" "memory allocation failed for options", FNAME); @@ -1137,7 +1410,7 @@ dhcp6_set_options(bp, ep, optinfo) memset(&pi, 0, sizeof(pi)); pi.dh6_pi_type = htons(DH6OPT_PREFIX_INFORMATION); pi.dh6_pi_len = htons(sizeof(pi) - 4); - pi.dh6_pi_duration = htonl(dp->val_prefix6.duration); + pi.dh6_pi_duration = htonl(dp->val_prefix6.vltime); pi.dh6_pi_plen = dp->val_prefix6.plen; memcpy(&pi.dh6_pi_paddr, &dp->val_prefix6.addr, sizeof(struct in6_addr)); @@ -1156,6 +1429,122 @@ dhcp6_set_options(bp, ep, optinfo) } #undef COPY_OPTION +/* + * Construct a DHCPv6 option along with sub-options in the wire format. + * If the packet buffer is NULL, just calculate the length of the option + * (and sub-options) so that the caller can allocate a buffer to store the + * option(s). + * This function basically assumes that the caller prepares enough buffer to + * store all the options. However, it also takes the buffer end and checks + * the possibility of overrun for safety. + */ +static int +copyout_option(p, ep, optval) + char *p, *ep; + struct dhcp6_listval *optval; +{ + struct dhcp6opt *opt; + struct dhcp6opt_stcode stcodeopt; + struct dhcp6opt_ia ia; + struct dhcp6opt_ia_pd_prefix pd_prefix; + char *subp; + struct dhcp6_listval *subov; + int optlen, headlen, sublen, opttype; + + /* check invariant for safety */ + if (p && ep <= p) + return (-1); + + /* first, detect the length of the option head */ + switch(optval->type) { + case DHCP6_LISTVAL_IAPD: + memset(&ia, 0, sizeof(ia)); + headlen = sizeof(ia); + opttype = DH6OPT_IA_PD; + opt = (struct dhcp6opt *)&ia; + break; + case DHCP6_LISTVAL_PREFIX6: + memset(&pd_prefix, 0, sizeof(pd_prefix)); + headlen = sizeof(pd_prefix); + opttype = DH6OPT_IA_PD_PREFIX; + opt = (struct dhcp6opt *)&pd_prefix; + break; + case DHCP6_LISTVAL_STCODE: + memset(&stcodeopt, 0, sizeof(stcodeopt)); + headlen = sizeof(stcodeopt); + opttype = DH6OPT_STATUS_CODE; + opt = (struct dhcp6opt *)&stcodeopt; + break; + default: + /* + * we encounter an unknown option. this should be an internal + * error. + */ + dprintf(LOG_ERR, "%s" "unknown option: code %d", FNAME, + optval->type); + return (-1); + } + + /* then, calculate the length of and/or fill in the sub-options */ + subp = NULL; + sublen = 0; + if (p) + subp = p + headlen; + for (subov = TAILQ_FIRST(&optval->sublist); subov; + subov = TAILQ_NEXT(subov, link)) { + int s; + + if ((s = copyout_option(subp, ep, subov)) < 0) + return (-1); + sublen += s; + } + + /* finally, deal with the head part again */ + optlen = headlen + sublen; + if (!p) + return(optlen); + + dprintf(LOG_DEBUG, "%s" "set %s", FNAME, dhcp6optstr(opttype)); + if (ep - p < headlen) /* check it just in case */ + return (-1); + + /* fill in the common part */ + opt->dh6opt_type = htons(opttype); + opt->dh6opt_len = htons(optlen - sizeof(struct dhcp6opt)); + + /* fill in type specific fields */ + switch(optval->type) { + case DHCP6_LISTVAL_IAPD: + ia.dh6_ia_iaid = htonl(optval->val_ia.iaid); + ia.dh6_ia_t1 = htonl(optval->val_ia.t1); + ia.dh6_ia_t2 = htonl(optval->val_ia.t2); + break; + case DHCP6_LISTVAL_PREFIX6: + pd_prefix.dh6_iapd_prefix_preferred_time = + htonl(optval->val_prefix6.pltime); + pd_prefix.dh6_iapd_prefix_valid_time = + htonl(optval->val_prefix6.vltime); + pd_prefix.dh6_iapd_prefix_prefix_len = + optval->val_prefix6.plen; + pd_prefix.dh6_iapd_prefix_prefix_addr = + optval->val_prefix6.addr; + break; + case DHCP6_LISTVAL_STCODE: + stcodeopt.dh6_stcode_code = htons(optval->val_num16); + break; + default: + /* + * XXX: this case should be rejected at the beginning of this + * function. + */ + return (-1); + } + + /* copyout the data (p must be non NULL at this point) */ + memcpy(p, opt, headlen); + return (optlen); +} + void dhcp6_set_timeoparam(ev) struct dhcp6_event *ev; @@ -1209,7 +1598,7 @@ dhcp6_reset_timer(ev) * The first Solicit message from the client on the interface * MUST be delayed by a random amount of time between * MIN_SOL_DELAY and MAX_SOL_DELAY. - * [dhcpv6-24 17.1.2] + * [dhcpv6-28 17.1.2] */ ev->retrans = (random() % (MAX_SOL_DELAY - MIN_SOL_DELAY)) + MIN_SOL_DELAY; @@ -1220,7 +1609,7 @@ dhcp6_reset_timer(ev) * The first RT MUST be selected to be strictly * greater than IRT by choosing RAND to be strictly * greater than 0. - * [dhcpv6-24 17.1.2] + * [dhcpv6-28 17.1.2] */ r = (double)((random() % 1000) + 1) / 10000; n = ev->init_retrans + r * ev->init_retrans; @@ -1304,6 +1693,7 @@ duidfree(duid) { if (duid->duid_id) free(duid->duid_id); + duid->duid_id = NULL; duid->duid_len = 0; } @@ -1335,6 +1725,10 @@ dhcp6optstr(type) return ("prefix delegation"); case DH6OPT_PREFIX_INFORMATION: return ("prefix information"); + case DH6OPT_IA_PD: + return ("IA_PD"); + case DH6OPT_IA_PD_PREFIX: + return ("IA_PD prefix"); default: sprintf(genstr, "opt_%d", type); return (genstr); @@ -1375,7 +1769,7 @@ dhcp6msgstr(type) char * dhcp6_stcodestr(code) - int code; + u_int16_t code; { static char genstr[sizeof("code255") + 1]; /* XXX thread unsafe */ @@ -1387,20 +1781,16 @@ dhcp6_stcodestr(code) return ("success"); case DH6OPT_STCODE_UNSPECFAIL: return ("unspec failure"); - case DH6OPT_STCODE_AUTHFAILED: - return ("auth fail"); - case DH6OPT_STCODE_ADDRUNAVAIL: - return ("address unavailable"); case DH6OPT_STCODE_NOADDRAVAIL: return ("no addresses"); case DH6OPT_STCODE_NOBINDING: return ("no binding"); - case DH6OPT_STCODE_CONFNOMATCH: - return ("confirm no match"); case DH6OPT_STCODE_NOTONLINK: return ("not on-link"); case DH6OPT_STCODE_USEMULTICAST: return ("use multicast"); + case DH6OPT_STCODE_NOPREFIXAVAIL: + return ("no prefixes"); default: sprintf(genstr, "code%d", code); return (genstr); diff --git a/kame/kame/dhcp6/common.h b/kame/kame/dhcp6/common.h index 246ed3ebc8..f437381fed 100644 --- a/kame/kame/dhcp6/common.h +++ b/kame/kame/dhcp6/common.h @@ -1,4 +1,4 @@ -/* $KAME: common.h,v 1.29 2002/06/11 08:24:34 jinmei Exp $ */ +/* $KAME: common.h,v 1.30 2003/01/05 17:12:12 jinmei Exp $ */ /* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. @@ -43,16 +43,17 @@ extern int foreground; extern int debug_thresh; extern char *device; +/* search option for dhcp6_find_listval() */ +#define MATCHLIST_PREFIXLEN 0x1 + /* common.c */ extern int dhcp6_copy_list __P((struct dhcp6_list *, struct dhcp6_list *)); extern void dhcp6_clear_list __P((struct dhcp6_list *)); -extern int dhcp6_count_list __P((struct dhcp6_list *)); +extern void dhcp6_clear_listval __P((struct dhcp6_listval *)); extern struct dhcp6_listval *dhcp6_find_listval __P((struct dhcp6_list *, - void *, - dhcp6_listval_type_t)); + dhcp6_listval_type_t, void *, int)); extern struct dhcp6_listval *dhcp6_add_listval __P((struct dhcp6_list *, - void *, - dhcp6_listval_type_t)); + dhcp6_listval_type_t, void *, struct dhcp6_list *)); extern struct dhcp6_event *dhcp6_create_event __P((struct dhcp6_if *, int)); extern void dhcp6_remove_event __P((struct dhcp6_event *)); extern int getifaddr __P((struct in6_addr *, char *, struct in6_addr *, @@ -81,7 +82,7 @@ extern void dhcp6_set_timeoparam __P((struct dhcp6_event *)); extern void dhcp6_reset_timer __P((struct dhcp6_event *)); extern char *dhcp6optstr __P((int)); extern char *dhcp6msgstr __P((int)); -extern char *dhcp6_stcodestr __P((int)); +extern char *dhcp6_stcodestr __P((u_int16_t)); extern char *duidstr __P((struct duid *)); extern int duidcpy __P((struct duid *, struct duid *)); extern int duidcmp __P((struct duid *, struct duid *)); diff --git a/kame/kame/dhcp6/config.c b/kame/kame/dhcp6/config.c index c7734e6afe..d87fc7621b 100644 --- a/kame/kame/dhcp6/config.c +++ b/kame/kame/dhcp6/config.c @@ -1,4 +1,4 @@ -/* $KAME: config.c,v 1.23 2002/12/29 00:36:31 jinmei Exp $ */ +/* $KAME: config.c,v 1.24 2003/01/05 17:12:12 jinmei Exp $ */ /* * Copyright (C) 2002 WIDE Project. @@ -51,26 +51,45 @@ extern int errno; struct dhcp6_if *dhcp6_if; struct prefix_ifconf *prefix_ifconflist; +struct iapd_conf *iapd_conflist; struct dhcp6_list dnslist; static struct dhcp6_ifconf *dhcp6_ifconflist; -static struct prefix_ifconf *prefix_ifconflist0; +static struct iapd_conf *iapd_conflist0; static struct host_conf *host_conflist0, *host_conflist; static struct dhcp6_list dnslist0; 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) */ + + struct dhcp6_list iapd_list; + struct dhcp6_list reqopt_list; +}; + extern struct cf_list *cf_dns_list; extern char *configfilename; +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 host_conf *, struct dhcp6_prefix *)); +static void clear_pd_pif __P((struct iapd_conf *)); static void clear_ifconf __P((struct dhcp6_ifconf *)); -static void clear_prefixifconf __P((struct prefix_ifconf *)); +static void clear_iaconf __P((struct ia_conf *)); static void clear_hostconf __P((struct host_conf *)); -static void clear_options __P((struct dhcp6_optconf *)); static int configure_duid __P((char *, struct duid *)); static int get_default_ifid __P((struct prefix_ifconf *)); +static struct ia_conf *find_iaconf_fromhead __P((struct ia_conf *, u_int32_t)); void ifinit(ifname) @@ -146,6 +165,7 @@ configure_interface(iflist) } ifc->server_pref = DH6OPT_PREF_UNDEF; + TAILQ_INIT(&ifc->iapd_list); TAILQ_INIT(&ifc->reqopt_list); for (cfl = ifp->params; cfl; cfl = cfl->next) { @@ -221,72 +241,152 @@ configure_interface(iflist) } int -configure_prefix_interface(iflist) - struct cf_namelist *iflist; +configure_ia(ialist, iatype) + struct cf_namelist *ialist; + iatype_t iatype; { - struct cf_namelist *ifp; - struct prefix_ifconf *pif; + struct cf_namelist *iap; + struct ia_conf *iac = NULL, **iac_head; + size_t confsize; + + switch(iatype) { + case IATYPE_PD: + confsize = sizeof(struct iapd_conf); + iac_head = (struct ia_conf **)&iapd_conflist0; + break; + default: + dprintf(LOG_ERR, "%s" "internal error", FNAME); + goto bad; + } - for (ifp = iflist; ifp; ifp = ifp->next) { + for (iap = ialist; iap; iap = iap->next) { struct cf_list *cfl; - if ((pif = malloc(sizeof(*pif))) == NULL) { + if ((iac = malloc(confsize)) == NULL) { dprintf(LOG_ERR, "%s" - "memory allocation for %s failed", FNAME, - ifp->name); - goto bad; - } - memset(pif, 0, sizeof(*pif)); - pif->next = prefix_ifconflist0; - prefix_ifconflist0 = pif; - - /* validate and copy ifname */ - if (if_nametoindex(ifp->name) == 0) { - dprintf(LOG_ERR, "%s" "invalid interface (%s): %s", - FNAME, ifp->name, strerror(errno)); - goto bad; - } - if ((pif->ifname = strdup(ifp->name)) == NULL) { - dprintf(LOG_ERR, "%s" "failed to copy ifname", FNAME); + "memory allocation for IA %s failed", FNAME, + iap->name); goto bad; } - - pif->ifid_len = IFID_LEN_DEFAULT; - pif->sla_len = SLA_LEN_DEFAULT; - if (get_default_ifid(pif)) { - dprintf(LOG_NOTICE, "%s" - "failed to get default IF ID for %s", - FNAME, pif->ifname); - goto bad; + memset(iac, 0, confsize); + + /* common initialization */ + iac->next = *iac_head; + *iac_head = iac; + iac->type = iatype; + iac->iaid = (u_int32_t)atoi(iap->name); + + /* IA-type specific initialization */ + switch(iatype) { + case IATYPE_PD: + TAILQ_INIT(&((struct iapd_conf *)iac)->iapd_pif_list); + break; } - for (cfl = ifp->params; cfl; cfl = cfl->next) { + /* set up parameters for the IA */ + for (cfl = iap->params; 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, "%s" - "invalid SLA length: %d", FNAME, - pif->sla_len); - goto bad; + case IACONF_PIF: + /* sanity check */ + if (iatype != IATYPE_PD) { + dprintf(LOG_ERR, "%s" "%s:%d " + "internal error " + "(IA type mismatch)", + FNAME, configfilename, cfl->line); } + if (add_pd_pif((struct iapd_conf *)iac, cfl)) + goto bad; break; default: dprintf(LOG_ERR, "%s" "%s:%d " - "invalid configuration", FNAME, - configfilename, cfl->line); + "invalid configuration", FNAME, + configfilename, cfl->line); goto bad; } } } - + return (0); bad: - /* there is currently nothing special to recover the error */ + 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, "%s" "%s:%d " + "duplicated prefix interface: %s", + FNAME, configfilename, cfl0->line, cfl0->ptr); + return (0); /* ignore it */ + } + } + + if ((pif = malloc(sizeof(*pif))) == NULL) { + dprintf(LOG_ERR, "%s" + "memory allocation for %s failed", FNAME, cfl0->ptr); + goto bad; + } + memset(pif, 0, sizeof(*pif)); + + /* validate and copy ifname */ + if (if_nametoindex(cfl0->ptr) == 0) { + dprintf(LOG_ERR, "%s" "%s:%d invalid interface (%s): %s", + FNAME, configfilename, cfl0->line, + cfl0->ptr, strerror(errno)); + goto bad; + } + if ((pif->ifname = strdup(cfl0->ptr)) == NULL) { + dprintf(LOG_ERR, "%s" "failed to copy ifname", FNAME); + goto bad; + } + + pif->ifid_len = IFID_LEN_DEFAULT; + pif->sla_len = SLA_LEN_DEFAULT; + if (get_default_ifid(pif)) { + dprintf(LOG_NOTICE, "%s" "failed to get default IF ID for %s", + FNAME, 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, "%s" "%s:%d " + "invalid SLA length: %d", FNAME, + configfilename, cfl->line, pif->sla_len); + goto bad; + } + break; + default: + dprintf(LOG_ERR, "%s" "%s:%d internal error: " + "invalid configuration", FNAME, + 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); } @@ -379,16 +479,16 @@ configure_global_option() TAILQ_INIT(&dnslist0); for (cl = cf_dns_list; cl; cl = cl->next) { /* duplication check */ - if (dhcp6_find_listval(&dnslist0, cl->ptr, - DHCP6_LISTVAL_ADDR6)) { + if (dhcp6_find_listval(&dnslist0, DHCP6_LISTVAL_ADDR6, + cl->ptr, 0)) { dprintf(LOG_INFO, "%s" "%s:%d duplicated DNS server: %s", FNAME, configfilename, cl->line, in6addr2str((struct in6_addr *)cl->ptr, 0)); goto bad; } - if (dhcp6_add_listval(&dnslist0, cl->ptr, - DHCP6_LISTVAL_ADDR6) == NULL) { + if (dhcp6_add_listval(&dnslist0, DHCP6_LISTVAL_ADDR6, + cl->ptr, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a DNS server"); goto bad; } @@ -523,10 +623,10 @@ get_default_ifid(pif) void configure_cleanup() { + clear_iaconf((struct ia_conf *)iapd_conflist0); + iapd_conflist0 = NULL; clear_ifconf(dhcp6_ifconflist); dhcp6_ifconflist = NULL; - clear_prefixifconf(prefix_ifconflist0); - prefix_ifconflist0 = NULL; clear_hostconf(host_conflist0); host_conflist0 = NULL; dhcp6_clear_list(&dnslist0); @@ -546,10 +646,9 @@ configure_commit() ifp->allow_flags = ifc->allow_flags; - clear_options(ifp->send_options); - - ifp->send_options = ifc->send_options; - ifc->send_options = NULL; + dhcp6_clear_list(&ifp->iapd_list); + ifp->iapd_list = ifc->iapd_list; + TAILQ_INIT(&ifc->iapd_list); dhcp6_clear_list(&ifp->reqopt_list); ifp->reqopt_list = ifc->reqopt_list; @@ -560,13 +659,13 @@ configure_commit() } clear_ifconf(dhcp6_ifconflist); - /* commit prefix configuration */ - if (prefix_ifconflist) { + /* commit IA_PD configuration */ + if (iapd_conflist) { /* clear previous configuration. (need more work?) */ - clear_prefixifconf(prefix_ifconflist); + clear_pd_pif(iapd_conflist); } - prefix_ifconflist = prefix_ifconflist0; - prefix_ifconflist0 = NULL; + iapd_conflist = iapd_conflist0; + iapd_conflist0 = NULL; /* commit prefix configuration */ if (host_conflist) { @@ -594,7 +693,7 @@ clear_ifconf(iflist) ifc_next = ifc->next; free(ifc->ifname); - clear_options(ifc->send_options); + dhcp6_clear_list(&ifc->iapd_list); dhcp6_clear_list(&ifc->reqopt_list); free(ifc); @@ -602,19 +701,37 @@ clear_ifconf(iflist) } static void -clear_prefixifconf(iflist) - struct prefix_ifconf *iflist; +clear_pd_pif(iapdc) + struct iapd_conf *iapdc; { struct prefix_ifconf *pif, *pif_next; - for (pif = iflist; pif; pif = pif_next) { - pif_next = 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); } } +static void +clear_iaconf(ialist) + struct ia_conf *ialist; +{ + struct ia_conf *iac, *iac_next; + + for (iac = ialist; iac; iac = iac_next) { + iac_next = iac->next; + + switch(iac->type) { + case IATYPE_PD: + clear_pd_pif((struct iapd_conf *)iac); + break; + } + free(iac); + } +} + static void clear_hostconf(hlist) struct host_conf *hlist; @@ -636,20 +753,6 @@ clear_hostconf(hlist) } } -static void -clear_options(opt0) - struct dhcp6_optconf *opt0; -{ - struct dhcp6_optconf *opt, *opt_next; - - for (opt = opt0; opt; opt = opt_next) { - opt_next = opt->next; - - free(opt->val); - free(opt); - } -} - static int add_options(opcode, ifc, cfl0) int opcode; @@ -659,6 +762,7 @@ add_options(opcode, ifc, cfl0) struct dhcp6_listval *opt; struct cf_list *cfl; int opttype; + struct dhcp6_ia ia; for (cfl = cfl0; cfl; cfl = cfl->next) { if (opcode == DHCPOPTCODE_REQUEST) { @@ -690,12 +794,51 @@ add_options(opcode, ifc, cfl0) return (-1); } break; + case DHCPOPT_IA_PD: + switch(opcode) { + case DHCPOPTCODE_SEND: + if (find_iaconf_fromhead( + (struct ia_conf *)iapd_conflist0, + (u_int32_t)cfl->num) == NULL) { + dprintf(LOG_ERR, "%s" "%s:%d " + "IA_PD (%lu) is not defined", + FNAME, configfilename, cfl->line, + (u_long)cfl->num); + return (-1); + } + + /* + * Set up IA parameters. Currently only + * IAID is configurable. + */ + memset(&ia, 0, sizeof(ia)); + ia.iaid = (u_int32_t)cfl->num; + + /* + * add the option information to the local + * configuration. + */ + if (dhcp6_add_listval(&ifc->iapd_list, + DHCP6_LISTVAL_IAPD, &ia, NULL) == NULL) { + dprintf(LOG_ERR, "%s" "failed to " + "configure an option", FNAME); + return (-1); + } + break; + default: + dprintf(LOG_ERR, "%s" "invalid operation (%d) " + "for option type (%d)", + FNAME, opcode, cfl->type); + break; + } + break; case DHCPOPT_PREFIX_DELEGATION: switch(opcode) { case DHCPOPTCODE_REQUEST: opttype = DH6OPT_PREFIX_DELEGATION; if (dhcp6_add_listval(&ifc->reqopt_list, - &opttype, DHCP6_LISTVAL_NUM) == NULL) { + DHCP6_LISTVAL_NUM, &opttype, NULL) + == NULL) { dprintf(LOG_ERR, "%s" "failed to " "configure an option", FNAME); return (-1); @@ -713,7 +856,8 @@ add_options(opcode, ifc, cfl0) case DHCPOPTCODE_REQUEST: opttype = DH6OPT_DNS; if (dhcp6_add_listval(&ifc->reqopt_list, - &opttype, DHCP6_LISTVAL_NUM) == NULL) { + DHCP6_LISTVAL_NUM, &opttype, NULL) + == NULL) { dprintf(LOG_ERR, "%s" "failed to " "configure an option", FNAME); return (-1); @@ -727,9 +871,10 @@ add_options(opcode, ifc, cfl0) } break; default: - dprintf(LOG_ERR, "%s" - "unknown option type: %d", FNAME, cfl->type); - return (-1); + dprintf(LOG_ERR, "%s" "%s:%d " + "unsupported option type: %d", + FNAME, configfilename, cfl->line, cfl->type); + return (-1); } next: @@ -744,7 +889,6 @@ add_prefix(hconf, prefix0) struct dhcp6_prefix *prefix0; { struct dhcp6_prefix oprefix; - struct dhcp6_listval *p, *pent; oprefix = *prefix0; @@ -775,27 +919,30 @@ add_prefix(hconf, prefix0) } /* prefix duplication check */ - for (p = TAILQ_FIRST(&hconf->prefix_list); p; - p = TAILQ_NEXT(p, link)) { - if (IN6_ARE_ADDR_EQUAL(&p->val_prefix6.addr, &oprefix.addr) && - p->val_prefix6.plen == oprefix.plen) { - dprintf(LOG_ERR, "%s" - "duplicated prefix: %s/%d for %s", FNAME, - in6addr2str(&oprefix.addr, 0), oprefix.plen, - hconf->name); - return (-1); - } + if (dhcp6_find_listval(&hconf->prefix_list, DHCP6_LISTVAL_PREFIX6, + &oprefix, 0)) { + dprintf(LOG_NOTICE, "%s" + "duplicated prefix: %s/%d for %s", FNAME, + in6addr2str(&oprefix.addr, 0), oprefix.plen, + hconf->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)) { + dprintf(LOG_NOTICE, "%s" "%s/%d has larger preferred lifetime " + "than valid lifetime", FNAME, + in6addr2str(&oprefix.addr, 0), oprefix.plen); + return (-1); } - /* allocate memory for the new prefix and insert it to the chain */ - if ((pent = malloc(sizeof(*pent))) == NULL) { - dprintf(LOG_ERR, "%s" "memory allocation failed for %s", - FNAME, hconf->name); + /* insert the new prefix to the chain */ + if (dhcp6_add_listval(&hconf->prefix_list, DHCP6_LISTVAL_PREFIX6, + &oprefix, NULL) == NULL) { return (-1); } - memset(pent, 0, sizeof(*pent)); - pent->val_prefix6 = oprefix; - TAILQ_INSERT_TAIL(&hconf->prefix_list, pent, link); return (0); } @@ -828,20 +975,39 @@ find_ifconfbyid(id) return (NULL); } -struct prefix_ifconf * -find_prefixifconf(ifname) - char *ifname; +static struct ia_conf * +find_iaconf_fromhead(head, iaid) + struct ia_conf *head; + u_int32_t iaid; { - struct prefix_ifconf *ifp; + struct ia_conf *iac; - for (ifp = prefix_ifconflist; ifp; ifp = ifp->next) { - if (strcmp(ifp->ifname, ifname) == NULL) - return (ifp); + for (iac = head; iac; iac = iac->next) { + if (iac->iaid == iaid) + return (iac); } return (NULL); } +struct ia_conf * +find_iaconf(type, iaid) + int type; + u_int32_t iaid; +{ + struct ia_conf *iac_head; + + switch(type) { + case IATYPE_PD: + iac_head = (struct ia_conf *)iapd_conflist; + break; + default: + return (NULL); + } + + return (find_iaconf_fromhead(iac_head, iaid)); +} + struct host_conf * find_hostconf(duid) struct duid *duid; diff --git a/kame/kame/dhcp6/config.h b/kame/kame/dhcp6/config.h index 7ab4d81c16..dc82f0d9fa 100644 --- a/kame/kame/dhcp6/config.h +++ b/kame/kame/dhcp6/config.h @@ -1,4 +1,4 @@ -/* $KAME: config.h,v 1.18 2002/06/14 15:32:55 jinmei Exp $ */ +/* $KAME: config.h,v 1.19 2003/01/05 17:12:12 jinmei Exp $ */ /* * Copyright (C) 2002 WIDE Project. @@ -54,7 +54,7 @@ struct dhcp6_if { int server_pref; /* server preference (server only) */ - struct dhcp6_optconf *send_options; + struct dhcp6_list iapd_list; struct dhcp6_list reqopt_list; struct dhcp6_serverinfo *current_server; @@ -83,7 +83,7 @@ struct dhcp6_event { TAILQ_HEAD(, dhcp6_eventdata) data_list; }; -typedef enum { DHCP6_DATA_PREFIX } dhcp6_eventdata_t; +typedef enum { DHCP6_EVDATA_IAPD } dhcp6_eventdata_t; struct dhcp6_eventdata { TAILQ_ENTRY(dhcp6_eventdata) link; @@ -91,6 +91,9 @@ struct dhcp6_eventdata { struct dhcp6_event *event; dhcp6_eventdata_t type; void *data; + + void (*destructor) __P((struct dhcp6_eventdata *)); + void *privdata; }; struct dhcp6_serverinfo { @@ -107,26 +110,9 @@ struct dhcp6_serverinfo { /* client status code */ enum {DHCP6S_INIT, DHCP6S_SOLICIT, DHCP6S_INFOREQ, DHCP6S_REQUEST, DHCP6S_RENEW, DHCP6S_REBIND, DHCP6S_IDLE}; - -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) */ - - struct dhcp6_optconf *send_options; - struct dhcp6_optconf *allow_options; - - struct dhcp6_list reqopt_list; -}; struct prefix_ifconf { - struct prefix_ifconf *next; + TAILQ_ENTRY(prefix_ifconf) link; char *ifname; /* interface name such as ne0 */ int sla_len; /* SLA ID length in bits */ @@ -138,27 +124,40 @@ struct prefix_ifconf { #define IFID_LEN_DEFAULT 64 #define SLA_LEN_DEFAULT 16 +struct ia_conf { + struct ia_conf *next; + int type; + u_int32_t iaid; + + /* type dependent values follow */ +}; +typedef enum {IATYPE_PD} iatype_t; + +TAILQ_HEAD(pifc_list, prefix_ifconf); +struct iapd_conf { + struct ia_conf iapd_ia; + + /* type dependent values follow */ + struct pifc_list iapd_pif_list; +}; +#define iapd_next iapd_ia.next +#define iapd_type iapd_ia.type +#define iapd_id iapd_ia.iaid + /* per-host configuration */ struct host_conf { struct host_conf *next; char *name; /* host name to identify the host */ struct duid duid; /* DUID for the host */ - /* delegated prefixes for the host: */ + /* delegated prefixes for the host */ + /* struct dhcp6_list prefix_list; */ struct dhcp6_list prefix_list; /* bindings of delegated prefixes */ struct dhcp6_list prefix_binding_list; }; -/* DHCP option information */ -struct dhcp6_optconf { - struct dhcp6_optconf *next; - int type; - int len; - char *val; -}; - /* structures and definitions used in the config file parser */ struct cf_namelist { struct cf_namelist *next; @@ -183,7 +182,9 @@ enum {DECL_SEND, DECL_ALLOW, DECL_INFO_ONLY, DECL_REQUEST, DECL_DUID, DECL_PREFIX, DECL_PREFERENCE, IFPARAM_SLA_ID, IFPARAM_SLA_LEN, DHCPOPT_RAPID_COMMIT, DHCPOPT_PREFIX_DELEGATION, DHCPOPT_DNS, - ADDRESS_LIST_ENT }; + DHCPOPT_IA_PD, + ADDRESS_LIST_ENT, + IACONF_PIF }; typedef enum {DHCP6_MODE_SERVER, DHCP6_MODE_CLIENT, DHCP6_MODE_RELAY } dhcp6_mode_t; @@ -197,8 +198,8 @@ extern struct dhcp6_list dnslist; extern void ifinit __P((char *)); extern int configure_interface __P((struct cf_namelist *)); -extern int configure_prefix_interface __P((struct cf_namelist *)); extern int configure_host __P((struct cf_namelist *)); +extern int configure_ia __P((struct cf_namelist *, iatype_t)); extern int configure_global_option __P((void)); extern void configure_cleanup __P((void)); extern void configure_commit __P((void)); @@ -209,3 +210,4 @@ extern struct prefix_ifconf *find_prefixifconf __P((char *)); extern struct host_conf *find_hostconf __P((struct duid *)); extern struct dhcp6_prefix *find_prefix6 __P((struct dhcp6_list *, struct dhcp6_prefix *)); +extern struct ia_conf *find_iaconf __P((int, u_int32_t)); diff --git a/kame/kame/dhcp6/configure b/kame/kame/dhcp6/configure index 1eb8c695ec..3220d1c183 100755 --- a/kame/kame/dhcp6/configure +++ b/kame/kame/dhcp6/configure @@ -832,8 +832,9 @@ Optional Packages: --with-opt-pdel=VALUE specify DHCP option value for prefix delegation --with-opt-pinfo=VALUE specify DHCP option value for prefix information --with-opt-preq=VALUE specify DHCP option value for prefix request - --with-opt-pa-pd=VALUE specify DHCP option value for IA_PD - --with-opt-preq=VALUE specify DHCP option value for IA_PD prefix + --with-opt-ia-pd=VALUE specify DHCP option value for IA_PD + --with-opt-ia-pd-prefix=VALUE specify DHCP option value for IA_PD prefix + --with-stcode-no-prefix=VALUE specify DHCP status code for NoPrefixAvail Some influential environment variables: CC C compiler command @@ -4321,6 +4322,26 @@ echo "${ECHO_T}using $dhcpopt_ia_pd_prefix" >&6 fi +echo "$as_me:$LINENO: checking for DHCP NoPrefixAvail status code" >&5 +echo $ECHO_N "checking for DHCP NoPrefixAvail status code... $ECHO_C" >&6 + +# Check whether --with-stcode-no-prefix or --without-stcode-no-prefix was given. +if test "${with_stcode_no_prefix+set}" = set; then + withval="$with_stcode_no_prefix" + dhcpstcode_no_prefix="$withval" +else + dhcpstcode_no_prefix=0 +fi; +if test $dhcpstcode_no_prefix = 0 ; then + dhcpstcode_no_prefix=6 + echo "$as_me:$LINENO: result: unspecified and using $dhcpstcode_no_prefix" >&5 +echo "${ECHO_T}unspecified and using $dhcpstcode_no_prefix" >&6 +else + echo "$as_me:$LINENO: result: using $dhcpstcode_no_prefix" >&5 +echo "${ECHO_T}using $dhcpstcode_no_prefix" >&6 +fi + + for ac_header in stdarg.h do @@ -5034,6 +5055,7 @@ s,@dhcpopt_pinfo@,$dhcpopt_pinfo,;t t s,@dhcpopt_preq@,$dhcpopt_preq,;t t s,@dhcpopt_ia_pd@,$dhcpopt_ia_pd,;t t s,@dhcpopt_ia_pd_prefix@,$dhcpopt_ia_pd_prefix,;t t +s,@dhcpstcode_no_prefix@,$dhcpstcode_no_prefix,;t t CEOF _ACEOF diff --git a/kame/kame/dhcp6/configure.in b/kame/kame/dhcp6/configure.in index feacc81e32..fc0b216037 100644 --- a/kame/kame/dhcp6/configure.in +++ b/kame/kame/dhcp6/configure.in @@ -191,7 +191,7 @@ AC_SUBST(dhcpopt_preq) AC_MSG_CHECKING(for DHCP IA_PD option) AC_ARG_WITH(opt-ia-pd, -[ --with-opt-pa-pd=VALUE specify DHCP option value for IA_PD], +[ --with-opt-ia-pd=VALUE specify DHCP option value for IA_PD], dhcpopt_ia_pd="$withval", dhcpopt_ia_pd=0) if test $dhcpopt_ia_pd = 0 ; then dhcpopt_ia_pd=33 @@ -203,7 +203,7 @@ AC_SUBST(dhcpopt_ia_pd) AC_MSG_CHECKING(for DHCP IA_PD_PREFIX option) AC_ARG_WITH(opt-ia-pd-prefix, -[ --with-opt-preq=VALUE specify DHCP option value for IA_PD prefix], +[ --with-opt-ia-pd-prefix=VALUE specify DHCP option value for IA_PD prefix], dhcpopt_ia_pd_prefix="$withval", dhcpopt_ia_pd_prefix=0) if test $dhcpopt_ia_pd_prefix = 0 ; then dhcpopt_ia_pd_prefix=34 @@ -213,6 +213,18 @@ else fi AC_SUBST(dhcpopt_ia_pd_prefix) +AC_MSG_CHECKING(for DHCP NoPrefixAvail status code) +AC_ARG_WITH(stcode-no-prefix, +[ --with-stcode-no-prefix=VALUE specify DHCP status code for NoPrefixAvail], + dhcpstcode_no_prefix="$withval", dhcpstcode_no_prefix=0) +if test $dhcpstcode_no_prefix = 0 ; then + dhcpstcode_no_prefix=6 + AC_MSG_RESULT(unspecified and using $dhcpstcode_no_prefix) +else + AC_MSG_RESULT(using $dhcpstcode_no_prefix) +fi +AC_SUBST(dhcpstcode_no_prefix) + AC_CHECK_HEADERS(stdarg.h) AC_OUTPUT(Makefile) diff --git a/kame/kame/dhcp6/dhcp6.h b/kame/kame/dhcp6/dhcp6.h index c9f28b9abc..a48982125f 100644 --- a/kame/kame/dhcp6/dhcp6.h +++ b/kame/kame/dhcp6/dhcp6.h @@ -1,4 +1,4 @@ -/* $KAME: dhcp6.h,v 1.33 2002/12/29 00:54:48 jinmei Exp $ */ +/* $KAME: dhcp6.h,v 1.34 2003/01/05 17:12:13 jinmei Exp $ */ /* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. @@ -27,9 +27,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -/* - * draft-ietf-dhc-dhcpv6-26 - */ #ifndef __DHCP6_H_DEFINED #define __DHCP6_H_DEFINED @@ -76,8 +73,6 @@ #define DHCP6_DURATITION_INFINITE 0xffffffff -/* Internal data structure */ - /* DUID: DHCP unique Identifier */ struct duid { int duid_len; /* length */ @@ -85,28 +80,45 @@ struct duid { }; /* option information */ -struct dhcp6_prefix { /* delegated prefix information */ - struct in6_addr addr; +struct dhcp6_ia { /* identity association */ + u_int32_t iaid; + u_int32_t t1; + u_int32_t t2; +}; + +struct dhcp6_prefix { + u_int32_t pltime; + u_int32_t vltime; int plen; - u_int32_t duration; + struct in6_addr addr; }; +/* Internal data structure */ +typedef enum { DHCP6_LISTVAL_NUM = 1, + DHCP6_LISTVAL_STCODE, DHCP6_LISTVAL_ADDR6, + DHCP6_LISTVAL_IAPD, DHCP6_LISTVAL_PREFIX6 +} dhcp6_listval_type_t; +TAILQ_HEAD(dhcp6_list, dhcp6_listval); struct dhcp6_listval { TAILQ_ENTRY(dhcp6_listval) link; + dhcp6_listval_type_t type; + union { int uv_num; + u_int16_t uv_num16; struct in6_addr uv_addr6; struct dhcp6_prefix uv_prefix6; + struct dhcp6_ia uv_ia; } uv; + + struct dhcp6_list sublist; }; #define val_num uv.uv_num +#define val_num16 uv.uv_num16 #define val_addr6 uv.uv_addr6 +#define val_ia uv.uv_ia #define val_prefix6 uv.uv_prefix6 -TAILQ_HEAD(dhcp6_list, dhcp6_listval); - -typedef enum { DHCP6_LISTVAL_NUM, DHCP6_LISTVAL_ADDR6, - DHCP6_LISTVAL_PREFIX6 } dhcp6_listval_type_t; struct dhcp6_optinfo { struct duid clientID; /* DUID */ @@ -115,7 +127,8 @@ struct dhcp6_optinfo { int rapidcommit; /* bool */ int pref; /* server preference */ - struct dhcp6_list reqopt_list; /* options in option request */ + struct dhcp6_list iapd_list; /* list of IA_PD */ + struct dhcp6_list reqopt_list; /* options in option request */ struct dhcp6_list stcode_list; /* status code */ struct dhcp6_list dns_list; /* DNS server list */ struct dhcp6_list prefix_list; /* prefix list */ @@ -151,13 +164,13 @@ struct dhcp6 { #define DH6OPT_STATUS_CODE 13 # define DH6OPT_STCODE_SUCCESS 0 # define DH6OPT_STCODE_UNSPECFAIL 1 -# define DH6OPT_STCODE_AUTHFAILED 2 -# define DH6OPT_STCODE_ADDRUNAVAIL 3 -# define DH6OPT_STCODE_NOADDRAVAIL 4 -# define DH6OPT_STCODE_NOBINDING 5 -# define DH6OPT_STCODE_CONFNOMATCH 6 -# define DH6OPT_STCODE_NOTONLINK 7 -# define DH6OPT_STCODE_USEMULTICAST 8 +# define DH6OPT_STCODE_NOADDRAVAIL 2 +# define DH6OPT_STCODE_NOBINDING 3 +# define DH6OPT_STCODE_NOTONLINK 4 +# define DH6OPT_STCODE_USEMULTICAST 5 +/* The following code is not yet defined and is currently KAME specific. */ +# define DH6OPT_STCODE_NOPREFIXAVAIL CONF_DH6OPT_STCODE_NOPREFIXAVAIL + #define DH6OPT_RAPID_COMMIT 14 #define DH6OPT_USER_CLASS 15 #define DH6OPT_VENDOR_CLASS 16 @@ -177,6 +190,10 @@ struct dhcp6 { #define DH6OPT_PREFIX_INFORMATION CONF_DH6OPT_PREFIX_INFORMATION #define DH6OPT_PREFIX_REQUEST CONF_DH6OPT_PREFIX_REQUEST +/* The following two are KAME specific. */ +#define DH6OPT_IA_PD CONF_DH6OPT_IA_PD +#define DH6OPT_IA_PD_PREFIX CONF_DH6OPT_IA_PD_PREFIX + struct dhcp6opt { u_int16_t dh6opt_type; u_int16_t dh6opt_len; @@ -184,15 +201,22 @@ struct dhcp6opt { } __attribute__ ((__packed__)); /* DUID type 1 */ -struct dhcp6_duid_type1 { - u_int16_t dh6duid1_type; - u_int16_t dh6duid1_hwtype; - u_int32_t dh6duid1_time; +struct dhcp6opt_duid_type1 { + u_int16_t dh6_duid1_type; + u_int16_t dh6_duid1_hwtype; + u_int32_t dh6_duid1_time; /* link-layer address follows */ } __attribute__ ((__packed__)); +/* Status Code */ +struct dhcp6opt_stcode { + u_int16_t dh6_stcode_type; + u_int16_t dh6_stcode_len; + u_int16_t dh6_stcode_code; +} __attribute__ ((__packed__)); + /* Prefix Information */ -struct dhcp6_prefix_info { +struct dhcp6opt_prefix_info { u_int16_t dh6_pi_type; u_int16_t dh6_pi_len; u_int32_t dh6_pi_duration; @@ -200,4 +224,28 @@ struct dhcp6_prefix_info { struct in6_addr dh6_pi_paddr; } __attribute__ ((__packed__)); +/* + * General format of Identity Association. + * This format applies to Prefix Delegation (IA_PD) and Non-temporary Addresses + * (IA_NA), while our implementation only supports the former. + */ +struct dhcp6opt_ia { + u_int16_t dh6_ia_type; + u_int16_t dh6_ia_len; + u_int32_t dh6_ia_iaid; + u_int32_t dh6_ia_t1; + u_int32_t dh6_ia_t2; + /* sub options follow */ +} __attribute__ ((__packed__)); + +/* IA_PD Prefix */ +struct dhcp6opt_ia_pd_prefix { + u_int16_t dh6_iapd_prefix_type; + u_int16_t dh6_iapd_prefix_len; + u_int32_t dh6_iapd_prefix_preferred_time; + u_int32_t dh6_iapd_prefix_valid_time; + u_int8_t dh6_iapd_prefix_prefix_len; + struct in6_addr dh6_iapd_prefix_prefix_addr; +} __attribute__ ((__packed__)); + #endif /*__DHCP6_H_DEFINED*/ diff --git a/kame/kame/dhcp6/dhcp6c.c b/kame/kame/dhcp6/dhcp6c.c index 11e661b790..d874850748 100644 --- a/kame/kame/dhcp6/dhcp6c.c +++ b/kame/kame/dhcp6/dhcp6c.c @@ -1,4 +1,4 @@ -/* $KAME: dhcp6c.c,v 1.98 2002/12/29 00:36:31 jinmei Exp $ */ +/* $KAME: dhcp6c.c,v 1.99 2003/01/05 17:12:13 jinmei Exp $ */ /* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. @@ -66,11 +66,12 @@ #include #include -#include -#include -#include -#include -#include +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" +#include "dhcp6c_ia.h" +#include "prefixconf.h" static int debug = 0; static u_long sig_flags = 0; @@ -373,7 +374,7 @@ client6_init() } ifp->outsock = outsock; - prefix6_init(); + init_ia(); if (signal(SIGHUP, client6_signal) == SIG_ERR) { dprintf(LOG_WARNING, "%s" "failed to set signal: %s", @@ -415,8 +416,8 @@ free_resources() { struct dhcp6_if *ifp; - /* release delegated prefixes (should send DHCPv6 release?) */ - prefix6_remove_all(); + /* release all IAs (should send DHCPv6 release accordingly?) */ + remove_all_ia(); for (ifp = dhcp6_if; ifp; ifp = ifp->next) { struct dhcp6_event *ev, *ev_next; @@ -518,6 +519,7 @@ client6_timo(arg) ev->state = DHCP6S_SOLICIT; dhcp6_set_timeoparam(ev); /* XXX */ /* fall through */ + case DHCP6S_REQUEST: case DHCP6S_INFOREQ: client6_send(ev); break; @@ -530,7 +532,7 @@ client6_timo(arg) client6_send_rebind(ev); } else { dprintf(LOG_INFO, "%s" - "all information to be updated were canceled", + "all information to be updated was canceled", FNAME); dhcp6_remove_event(ev); return (NULL); @@ -565,7 +567,7 @@ select_server(ifp) struct dhcp6_serverinfo *s; /* - * pick the best server according to dhcpv6-26 Section 17.1.3 + * pick the best server according to dhcpv6-28 Section 17.1.3 * XXX: we currently just choose the one that is active and has the * highest preference. */ @@ -654,6 +656,7 @@ client6_send(ev) struct dhcp6 *dh6; struct dhcp6_optinfo optinfo; ssize_t optlen, len; + struct dhcp6_listval *iapd; ifp = ev->ifp; @@ -685,7 +688,7 @@ client6_send(ev) * each new message it sends. * * A client MUST leave the transaction-ID unchanged in - * retransmissions of a message. [dhcpv6-26 15.1] + * retransmissions of a message. [dhcpv6-28 15.1] */ #ifdef HAVE_ARC4RANDOM ev->xid = arc4random() & DH6_XIDMASK; @@ -726,6 +729,44 @@ client6_send(ev) optinfo.rapidcommit = 1; } + /* IA_PD */ + switch (ev->state) { + case DHCP6S_SOLICIT: + if (dhcp6_copy_list(&optinfo.iapd_list, &ifp->iapd_list)) { + dprintf(LOG_NOTICE, "%s" + "failed to copy options to be sent", + FNAME); + goto end; + } + break; + case DHCP6S_REQUEST: + /* + * First check the IA_PD given in the advertise. + * If the server has given an IA_PD for a local IA, include + * the given IA. Otherwise, include locally configured IA + * anyway. + * (Specification is not clear on this policy.) + */ + for (iapd = TAILQ_FIRST(&ifp->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + struct dhcp6_listval *v; + + if ((v = dhcp6_find_listval( + &ifp->current_server->optinfo.iapd_list, + DHCP6_LISTVAL_IAPD, &iapd->uv, 0)) == NULL) + v = iapd; + + if (dhcp6_add_listval(&optinfo.iapd_list, + DHCP6_LISTVAL_IAPD, &v->uv, &v->sublist) == NULL) { + dprintf(LOG_NOTICE, "%s" + "failed to construct IA_PD for request", + FNAME); + goto end; + } + } + break; + } + /* option request options */ if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) { dprintf(LOG_ERR, "%s" "failed to copy requested options", @@ -746,18 +787,18 @@ client6_send(ev) /* set options in the message */ if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1), - (struct dhcp6opt *)(buf + sizeof(buf)), - &optinfo)) < 0) { + (struct dhcp6opt *)(buf + sizeof(buf)), &optinfo)) < 0) { dprintf(LOG_INFO, "%s" "failed to construct options", FNAME); goto end; } len += optlen; /* - * Unless otherwise specified, a client sends DHCP messages to the - * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. - * [dhcpv6-26 Section 13.] - * Our current implementation always follows the case. + * Unless otherwise specified in this document or in a document that + * describes how IPv6 is carried over a specific type of link (for link + * types that do not support multicast), a client sends DHCP messages + * to the All_DHCP_Relay_Agents_and_Servers. + * [dhcpv6-28 Section 13.] */ dst = *sa6_allagent; dst.sin6_scope_id = ifp->linkid; @@ -790,7 +831,7 @@ client6_send_renew(ev) struct sockaddr_in6 dst; ifp = ev->ifp; - + dh6 = (struct dhcp6 *)buf; memset(dh6, 0, sizeof(*dh6)); dh6->dh6_msgtype = DH6_RENEW; @@ -828,12 +869,11 @@ client6_send_renew(ev) for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = TAILQ_NEXT(evd, link)) { switch(evd->type) { - case DHCP6_DATA_PREFIX: - if (dhcp6_add_listval(&optinfo.prefix_list, - &((struct dhcp6_siteprefix *)evd->data)->prefix, - DHCP6_LISTVAL_PREFIX6) == NULL) { - dprintf(LOG_ERR, "%s" "failed to add a " - "prefix", FNAME); + case DHCP6_EVDATA_IAPD: + if (dhcp6_copy_list(&optinfo.iapd_list, + (struct dhcp6_list *)evd->data)) { + dprintf(LOG_NOTICE, "%s" "failed to add " + "an IAPD", FNAME); goto end; } break; @@ -854,10 +894,11 @@ client6_send_renew(ev) len += optlen; /* - * Unless otherwise specified, a client sends DHCP messages to the - * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. - * [dhcpv6-26 Section 13.] - * Our current implementation always follows the case. + * Unless otherwise specified in this document or in a document that + * describes how IPv6 is carried over a specific type of link (for link + * types that do not support multicast), a client sends DHCP messages + * to the All_DHCP_Relay_Agents_and_Servers. + * [dhcpv6-28 Section 13.] */ dst = *sa6_allagent; dst.sin6_scope_id = ifp->linkid; @@ -922,12 +963,11 @@ client6_send_rebind(ev) for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = TAILQ_NEXT(evd, link)) { switch(evd->type) { - case DHCP6_DATA_PREFIX: - if (dhcp6_add_listval(&optinfo.prefix_list, - &((struct dhcp6_siteprefix *)evd->data)->prefix, - DHCP6_LISTVAL_PREFIX6) == NULL) { - dprintf(LOG_ERR, "%s" "failed to add a " - "prefix", FNAME); + case DHCP6_EVDATA_IAPD: + if (dhcp6_copy_list(&optinfo.iapd_list, + (struct dhcp6_list *)evd->data)) { + dprintf(LOG_NOTICE, "%s" "failed to add " + "an IAPD", FNAME); goto end; } break; @@ -949,10 +989,11 @@ client6_send_rebind(ev) len += optlen; /* - * Unless otherwise specified, a client sends DHCP messages to the - * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address. - * [dhcpv6-26 Section 13.] - * Our current implementation always follows the case. + * Unless otherwise specified in this document or in a document that + * describes how IPv6 is carried over a specific type of link (for link + * types that do not support multicast), a client sends DHCP messages + * to the All_DHCP_Relay_Agents_and_Servers. + * [dhcpv6-28 Section 13.] */ dst = *sa6_allagent; dst.sin6_scope_id = ifp->linkid; @@ -1078,13 +1119,7 @@ client6_recvadvert(ifp, dh6, len, optinfo0) return (-1); } - if (ev->state != DHCP6S_SOLICIT || - (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { - dprintf(LOG_INFO, "%s" "unexpected advertise", FNAME); - return (-1); - } - - /* packet validation based on Section 15.3 of dhcpv6-26. */ + /* packet validation based on Section 15.3 of dhcpv6-28. */ if (optinfo0->serverID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no server ID option", FNAME); return (-1); @@ -1102,14 +1137,44 @@ client6_recvadvert(ifp, dh6, len, optinfo0) return (-1); } + /* + * The requesting router MUST ignore any Advertise message that + * includes a Status Code option containing the value NoPrefixAvail. + * [dhc-dhcpv6-opt-prefix-delegation-01 Section 10.1]. + * We only apply this when we are requiring prefixes to be delegated. + */ + if (!TAILQ_EMPTY(&ifp->iapd_list)) { + u_int16_t stcode = DH6OPT_STCODE_NOPREFIXAVAIL; + + if (dhcp6_find_listval(&optinfo0->stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, 0)) { + dprintf(LOG_INFO, "%s" + "advertise contains NoPrefixAvail status", FNAME); + return (-1); + } + } + /* * The client MUST ignore any Advertise message that includes a Status * Code option containing the value NoAddrsAvail. - * [dhcpv6-26, Section 17.1.3]. + * [dhcpv6-28, Section 17.1.3]. * XXX: we should not follow this when we do not need addresses!! */ ; + if (ev->state != DHCP6S_SOLICIT || + (ifp->send_flags & DHCIFF_RAPID_COMMIT)) { + /* + * We expect a reply message, but does actually receives an + * Advertise message. The server should not be configured to + * allow the Rapid Commit option. + * We process the message as if we expected the Advertise. + * [dhcpv6-28 Section 17.1.3] + */ + dprintf(LOG_INFO, "%s" "unexpected advertise", FNAME); + /* proceed anyway */ + } + /* ignore the server if it is known */ if (find_server(ifp, &optinfo0->serverID)) { dprintf(LOG_INFO, "%s" "duplicated server (ID: %s)", @@ -1206,6 +1271,7 @@ client6_recvreply(ifp, dh6, len, optinfo) { struct dhcp6_listval *lv; struct dhcp6_event *ev; + struct dhcp6_eventdata *evd, *evd_next; /* find the corresponding event based on the received xid */ ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK); @@ -1243,15 +1309,29 @@ client6_recvreply(ifp, dh6, len, optinfo) return (-1); } + /* + * If the client included a Rapid Commit option in the Solicit message, + * the client discards any Reply messages it receives that do not + * include a Rapid Commit option. + * (should we keep the server otherwise?) + * [dhcpv6-28 Section 17.1.4] + */ + if (ev->state == DHCP6S_SOLICIT && + (ifp->send_flags & DHCIFF_RAPID_COMMIT) && + !optinfo->rapidcommit) { + dprintf(LOG_INFO, "%s" "no rapid commit", FNAME); + return (-1); + } + /* * The client MAY choose to report any status code or message from the * status code option in the Reply message. - * [dhcpv6-26 Section 18.1.8] + * [dhcpv6-28 Section 18.1.8] */ for (lv = TAILQ_FIRST(&optinfo->stcode_list); lv; lv = TAILQ_NEXT(lv, link)) { dprintf(LOG_INFO, "%s" "status code: %s", - FNAME, dhcp6_stcodestr(lv->val_num)); + FNAME, dhcp6_stcodestr(lv->val_num16)); } if (!TAILQ_EMPTY(&optinfo->dns_list)) { @@ -1265,19 +1345,8 @@ client6_recvreply(ifp, dh6, len, optinfo) } } - if (ev->state == DHCP6S_RENEW || ev->state == DHCP6S_REBIND) { - /* - * Update configuration information to be renewed or rebound. - * Note that the returned list may be empty, in which case - * the waiting information should be removed. - */ - prefix6_update(ev, &optinfo->prefix_list, &optinfo->serverID); - } else { - for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; - lv = TAILQ_NEXT(lv, link)) { - prefix6_add(ifp, &lv->val_prefix6, &optinfo->serverID); - } - } + /* update stateful configuration information */ + update_ia(IATYPE_PD, &optinfo->iapd_list, ifp, &optinfo->serverID); dhcp6_remove_event(ev); dprintf(LOG_DEBUG, "%s" "got an expected reply, sleeping.", FNAME); diff --git a/kame/kame/dhcp6/dhcp6c_ia.c b/kame/kame/dhcp6/dhcp6c_ia.c new file mode 100644 index 0000000000..256496d61b --- /dev/null +++ b/kame/kame/dhcp6/dhcp6c_ia.c @@ -0,0 +1,475 @@ +/* $KAME: dhcp6c_ia.c,v 1.1 2003/01/05 17:12:13 jinmei Exp $ */ + +/* + * Copyright (C) 2003 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 +#include +#include + +#include + +#include +#include +#include + +#include "dhcp6.h" +#include "config.h" +#include "common.h" +#include "timer.h" +#include "dhcp6c_ia.h" +#include "prefixconf.h" + +typedef enum {IAS_ACTIVE, IAS_RENEW, IAS_REBIND} iastate_t; + +struct ia { + TAILQ_ENTRY(ia) link; + + /* identifier of this IA */ + iatype_t iatype; + u_int32_t iaid; + + /* common parameters of IA */ + u_int32_t t1; /* duration for renewal */ + u_int32_t t2; /* duration for rebind */ + + /* internal parameters for renewal/rebinding */ + iastate_t state; + struct dhcp6_timer *timer; + struct dhcp6_eventdata *evdata; + + /* DHCP related parameters */ + struct dhcp6_if *ifp; /* DHCP interface */ + struct duid serverid; /* the server ID that provided this IA */ + + struct iactl *ctl; +}; +static TAILQ_HEAD(, ia) ia_listhead; + +static void callback __P((struct ia *)); +static void remove_ia __P((struct ia *)); +static struct ia *find_ia __P((iatype_t, u_int32_t)); +static struct dhcp6_timer *ia_timo __P((void *)); + +static char *iastr __P((iatype_t)); +static char *statestr __P((iastate_t)); + +extern struct dhcp6_timer *client6_timo __P((void *)); +extern void client6_send_renew __P((struct dhcp6_event *)); +extern void client6_send_rebind __P((struct dhcp6_event *)); + +void +init_ia() +{ + TAILQ_INIT(&ia_listhead); +} + +void +update_ia(iatype, ialist, ifp, serverid) + iatype_t iatype; + struct dhcp6_list *ialist; + struct dhcp6_if *ifp; + struct duid *serverid; +{ + struct ia *ia; + struct ia_conf *iac; + struct iapd_conf *iapdc; + struct dhcp6_listval *iav, *siav, *convf; + struct timeval timo; + struct duid newserver; + + for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) { + /* if we're not interested in this IA, ignore it. */ + if ((iac = find_iaconf(iatype, iav->val_ia.iaid)) == NULL) + continue; + + /* locate or make the local IA */ + if ((ia = find_ia(iatype, iav->val_ia.iaid)) == NULL) { + if ((ia = malloc(sizeof(*ia))) == NULL) { + dprintf(LOG_NOTICE, "%s" "memory allocation " + "failed", FNAME); + return; /* XXX */ + } + memset(ia, 0, sizeof(*ia)); + ia->iatype = iatype; + ia->iaid = iav->val_ia.iaid; + ia->state = IAS_ACTIVE; + + TAILQ_INSERT_TAIL(&ia_listhead, ia, link); + } + + /* update IA parameters */ + ia->t1 = iav->val_ia.t1; + ia->t2 = iav->val_ia.t2; + if (ia->t1 > ia->t2) { + dprintf(LOG_INFO, "%s" "invalid IA: T1(%lu) > T2(%lu)", + FNAME, ia->t1, ia->t2); + continue; /* XXX: or should we try to recover? */ + } + ia->ifp = ifp; + if (duidcpy(&newserver, serverid)) { + dprintf(LOG_NOTICE, "%s" "failed to copy server ID", + FNAME); + continue; + } + duidfree(&ia->serverid); + ia->serverid = newserver; + + /* update IA configuration information */ + for (siav = TAILQ_FIRST(&iav->sublist); siav; + siav = TAILQ_NEXT(siav, link)) { + switch (siav->type) { + case DHCP6_LISTVAL_PREFIX6: + /* add or update the prefix */ + iapdc = (struct iapd_conf *)iac; + if (update_prefix(ia, &siav->val_prefix6, + &iapdc->iapd_pif_list, ifp, &ia->ctl, + callback)) { + dprintf(LOG_NOTICE, "%s" + "failed to update a prefix %s/%d", + FNAME, + in6addr2str(&siav->val_prefix6.addr, 0), + siav->val_prefix6.plen); + } + break; + case DHCP6_LISTVAL_STCODE: + dprintf(LOG_INFO, "%s" + "status code for %s-%lu: %s", FNAME, + iastr(iatype), iav->val_ia.iaid, + dhcp6_stcodestr(siav->val_num16)); + if ((ia->state == IAS_RENEW || + ia->state == IAS_REBIND) && + siav->val_num16 == DH6OPT_STCODE_NOBINDING) { + /* + * When the client receives a NoBinding + * status in an IA from the server + * in response to a Renew message or + * a Rebind message, the client sends + * a Request to reestablish an IA with + * the server. + * [dhcpv6-28 Section 18.1.8] + * XXX: what about the PD case? + */ + dprintf(LOG_INFO, "%s" + "receive NoBinding against " + "renew/rebind for %s-%lu", FNAME, + iastr(ia->iatype), ia->iaid); + remove_ia(ia); + goto nextia; + } + break; + } + } + + /* see if this IA is still valid. if not, remove it. */ + if (ia->ctl == NULL || !(*ia->ctl->isvalid)(ia->ctl)) { + dprintf(LOG_DEBUG, "%s" "IA %s-%lu is invalidated", + FNAME, iastr(ia->iatype), ia->iaid); + remove_ia(ia); + continue; + } + + /* if T1 or T2 is 0, determine appropriate values locally. */ + if (ia->t1 == 0 || ia->t2 == 0) { + u_int32_t duration; + + if (ia->ctl && ia->ctl->duration) { + duration = (*ia->ctl->duration)(ia->ctl); + } else + duration = 1800; /* 30min. XXX: no rationale */ + + if (ia->t1 == 0) { + if (duration == DHCP6_DURATITION_INFINITE) + ia->t1 = DHCP6_DURATITION_INFINITE; + else + ia->t1 = duration / 2; + } + if (ia->t2 == 0) { + if (duration == DHCP6_DURATITION_INFINITE) + ia->t2 = DHCP6_DURATITION_INFINITE; + else + ia->t2 = duration * 4 / 5; + } + + /* make sure T1 <= T2 */ + if (ia->t1 > ia->t2) + ia->t1 = ia->t2 * 5 / 8; + + dprintf(LOG_INFO, "%s" "T1(%lu) and/or T2(%lu) " + "is locally determined", FNAME, ia->t1, ia->t2); + } + + /* set up a timer for this IA. */ + if (ia->t1 == DHCP6_DURATITION_INFINITE) { + if (ia->timer) + dhcp6_remove_timer(&ia->timer); + } else { + if (ia->timer == NULL) + ia->timer = dhcp6_add_timer(ia_timo, ia); + if (ia->timer == NULL) { + dprintf(LOG_ERR, "%s" "failed to add IA " + "timer", FNAME); + remove_ia(ia); /* XXX */ + continue; + } + timo.tv_sec = ia->t1; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, ia->timer); + } + + ia->state = IAS_ACTIVE; + + nextia: + } +} + +static void +callback(ia) + struct ia *ia; +{ + /* see if this IA is still valid. if not, remove it. */ + if (ia->ctl == NULL || !(*ia->ctl->isvalid)(ia->ctl)) { + dprintf(LOG_DEBUG, "%s" "IA %s-%lu is invalidated", + FNAME, iastr(ia->iatype), ia->iaid); + remove_ia(ia); + } +} + +void +remove_all_ia() +{ + struct ia *ia, *ia_next; + + for (ia = TAILQ_FIRST(&ia_listhead); ia; ia = ia_next) { + ia_next = TAILQ_NEXT(ia, link); + + remove_ia(ia); + } +} + +static void +remove_ia(ia) + struct ia *ia; +{ + dprintf(LOG_DEBUG, "%s" "remove an IA: %s-%lu", FNAME, + iastr(ia->iatype), ia->iaid); + + TAILQ_REMOVE(&ia_listhead, ia, link); + + if (ia->timer) + dhcp6_remove_timer(&ia->timer); + + if (ia->evdata) { + TAILQ_REMOVE(&ia->evdata->event->data_list, ia->evdata, link); + if (ia->evdata->destructor) + ia->evdata->destructor(ia->evdata); + else + free(ia->evdata); + ia->evdata = NULL; + } + + if (ia->ctl && ia->ctl->cleanup) + (*ia->ctl->cleanup)(ia->ctl); + + free(ia); +} + +static struct dhcp6_timer * +ia_timo(arg) + void *arg; +{ + struct ia *ia = (struct ia *)arg; + struct dhcp6_ia iaparam; + struct dhcp6_event *ev; + struct dhcp6_eventdata *evd; + struct timeval timo; + int dhcpstate; + + dprintf(LOG_DEBUG, "%s" "IA timeout for %s-%lu, state=%s", FNAME, + iastr(ia->iatype), ia->iaid, statestr(ia->state)); + + /* cancel the current event for the prefix. */ + if (ia->evdata) { + TAILQ_REMOVE(&ia->evdata->event->data_list, ia->evdata, link); + if (ia->evdata->destructor) + ia->evdata->destructor(ia->evdata); + else + free(ia->evdata); + ia->evdata = NULL; + } + + switch (ia->state) { + case IAS_ACTIVE: + ia->state = IAS_RENEW; + dhcpstate = DHCP6S_RENEW; + timo.tv_sec = ia->t1 < ia->t2 ? ia->t2 - ia->t1 : 0; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, ia->timer); + break; + case IAS_RENEW: + ia->state = IAS_REBIND; + dhcpstate = DHCP6S_REBIND; + duidfree(&ia->serverid); + + /* + * We don't need a timer for the IA. We'll just wait for a + * reply for the REBIND until all associated configuration + * information for this IA expire. + */ + dhcp6_remove_timer(&ia->timer); + break; + default: + dprintf(LOG_ERR, "%s" "invalid IA state (%d)", + FNAME, (int)ia->state); + return (NULL); /* XXX */ + } + + if ((ev = dhcp6_create_event(ia->ifp, dhcpstate)) == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to create a new event" + FNAME); + goto fail; + } + if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to create a new event " + "timer", FNAME); + goto fail; + } + if ((evd = malloc(sizeof(*evd))) == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to create a new event " + "data", FNAME); + goto fail; + } + if (ia->state == IAS_RENEW) { + if (duidcpy(&ev->serverid, &ia->serverid)) { + dprintf(LOG_NOTICE, "%s" "failed to copy server ID", + FNAME); + goto fail; + } + } + memset(evd, 0, sizeof(*evd)); + iaparam.iaid = ia->iaid; + iaparam.t1 = ia->t1; + iaparam.t2 = ia->t2; + switch(ia->state) { + case IAS_RENEW: + if (ia->ctl && ia->ctl->renew_data) + if ((*ia->ctl->renew_data)(ia->ctl, &iaparam, + &ia->evdata, evd)) { + dprintf(LOG_NOTICE, "%s" "failed to make " + "renew data", FNAME); + goto fail; + } + break; + case IAS_REBIND: + if (ia->ctl && ia->ctl->rebind_data) + if ((*ia->ctl->rebind_data)(ia->ctl, &iaparam, + &ia->evdata, evd)) { + dprintf(LOG_NOTICE, "%s" "failed to make " + "rebind data", FNAME); + goto fail; + } + break; + } + evd->event = ev; + TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + + TAILQ_INSERT_TAIL(&ia->ifp->event_list, ev, link); + + ev->timeouts = 0; + dhcp6_set_timeoparam(ev); + dhcp6_reset_timer(ev); + + ia->evdata = evd; + + switch(ia->state) { + case IAS_RENEW: + client6_send_renew(ev); + break; + case IAS_REBIND: + client6_send_rebind(ev); + break; + case IAS_ACTIVE: + /* what to do? */ + break; + } + + return (ia->timer); + + fail: + if (ev && ev->timer) + dhcp6_remove_timer(&ev->timer); + if (ev && &ev->serverid) + duidfree(&ev->serverid); + if (ev) + free(ev); + return (NULL); +} + +struct ia * +find_ia(type, iaid) + iatype_t type; + u_int32_t iaid; +{ + struct ia *ia; + + for (ia = TAILQ_FIRST(&ia_listhead); ia; + ia = TAILQ_NEXT(ia, link)) { + if (ia->iatype == type && ia->iaid == iaid) + return (ia); + } + + return (NULL); +} + +static char * +iastr(type) + iatype_t type; +{ + switch (type) { + case IATYPE_PD: + return ("PD"); + default: + return ("???"); /* should be a bug */ + } +} + +static char * +statestr(state) + iastate_t state; +{ + switch (state) { + case IAS_ACTIVE: + return "ACTIVE"; + case IAS_RENEW: + return "RENEW"; + case IAS_REBIND: + return "REBIND"; + default: + return "???"; /* should be a bug */ + } +} diff --git a/kame/kame/dhcp6/dhcp6c_ia.h b/kame/kame/dhcp6/dhcp6c_ia.h new file mode 100644 index 0000000000..8543b62b06 --- /dev/null +++ b/kame/kame/dhcp6/dhcp6c_ia.h @@ -0,0 +1,53 @@ +/* $KAME: dhcp6c_ia.h,v 1.1 2003/01/05 17:12:13 jinmei Exp $ */ + +/* + * Copyright (C) 2003 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. + */ + +struct ia; /* this is an opaque type */ + +struct iactl { + struct ia *iactl_ia; /* back pointer to IA */ + + /* callback function called when something may happen on the IA */ + void (*callback) __P((struct ia *)); + + /* common methods: */ + int (*isvalid) __P((struct iactl *)); + u_int32_t (*duration) __P((struct iactl *)); + int (*renew_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + int (*rebind_data) __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); + void (*cleanup) __P((struct iactl *)); +}; + +extern void init_ia __P((void)); +extern void update_ia __P((iatype_t, struct dhcp6_list *, + struct dhcp6_if *, struct duid *)); +extern void remove_all_ia __P((void)); diff --git a/kame/kame/dhcp6/dhcp6s.c b/kame/kame/dhcp6/dhcp6s.c index dc58787a8f..2b27cfad5b 100644 --- a/kame/kame/dhcp6/dhcp6s.c +++ b/kame/kame/dhcp6/dhcp6s.c @@ -1,4 +1,4 @@ -/* $KAME: dhcp6s.c,v 1.93 2002/12/29 00:54:48 jinmei Exp $ */ +/* $KAME: dhcp6s.c,v 1.94 2003/01/05 17:12:13 jinmei Exp $ */ /* * Copyright (C) 1998 and 1999 WIDE Project. * All rights reserved. @@ -70,16 +70,30 @@ #include #include -typedef enum { DHCP6_CONFINFO_PREFIX } dhcp6_conftype_t; +typedef enum { DHCP6_BINDING_IA } dhcp6_bindingtype_t; struct dhcp6_binding { TAILQ_ENTRY(dhcp6_binding) link; - dhcp6_conftype_t type; + dhcp6_bindingtype_t type; + + /* identifier of the binding */ struct duid clientid; - void *val; + /* additional identifiers for IA-based bindings */ + int iatype; + u_int32_t iaid; + + /* + * configuration information of this binding, + * which is type-dependent. + */ + union { + struct dhcp6_list uv_list; + } val; +#define val_list val.uv_list u_int32_t duration; + time_t updatetime; struct dhcp6_timer *timer; }; static TAILQ_HEAD(, dhcp6_binding) dhcp6_binding_head; @@ -127,15 +141,24 @@ static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *, struct dhcp6_optinfo *, struct sockaddr *, int, struct dhcp6_optinfo *)); -static int create_conflist __P((dhcp6_conftype_t, struct duid *, - struct dhcp6_list *, struct dhcp6_list *, - struct dhcp6_list *, int)); +static int make_ia_stcode __P((int, u_int32_t, u_int16_t, + struct dhcp6_list *)); +static int make_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_optinfo *)); +static int make_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_list *, struct host_conf *, int)); +static int make_match_ia __P((struct dhcp6_listval *, struct dhcp6_list *, + struct dhcp6_list *)); +static void calc_ia_timo __P((struct dhcp6_ia *, struct dhcp6_list *, + struct host_conf *)); +static void update_binding_duration __P((struct dhcp6_binding *)); static struct dhcp6_binding *add_binding __P((struct duid *, - dhcp6_conftype_t, void *)); + dhcp6_bindingtype_t, int, u_int32_t, void *)); static struct dhcp6_binding *find_binding __P((struct duid *, - dhcp6_conftype_t, void *)); + dhcp6_bindingtype_t, int, u_int32_t)); static void update_binding __P((struct dhcp6_binding *)); static void remove_binding __P((struct dhcp6_binding *)); +static void free_binding __P((struct dhcp6_binding *)); static struct dhcp6_timer *binding_timo __P((void *)); static char *bindingstr __P((struct dhcp6_binding *)); @@ -517,6 +540,7 @@ server6_recv(s) dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&from)); + /* * A server MUST discard any Solicit, Confirm, Rebind or * Information-request messages it receives with a unicast @@ -589,7 +613,7 @@ server6_react_solicit(ifp, dh6, optinfo, from, fromlen) /* * Servers MUST discard any Solicit messages that do not include a - * Client Identifier option. [dhcpv6-26 Section 15.2] + * Client Identifier option. [dhcpv6-28 Section 15.2] */ if (optinfo->clientID.duid_len == 0) { dprintf(LOG_INFO, "%s" "no client ID option", FNAME); @@ -639,17 +663,56 @@ server6_react_solicit(ifp, dh6, optinfo, from, fromlen) */ if (optinfo->rapidcommit && (ifp->allow_flags & DHCIFF_RAPID_COMMIT)) do_binding = 1; - for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt; - opt = TAILQ_NEXT(opt, link)) { - switch(opt->val_num) { - case DH6OPT_PREFIX_DELEGATION: - create_conflist(DHCP6_CONFINFO_PREFIX, - &optinfo->clientID, &roptinfo.prefix_list, - client_conf ? &client_conf->prefix_list : NULL, - TAILQ_EMPTY(&optinfo->prefix_list) ? - NULL : &optinfo->prefix_list, - do_binding); - break; + + /* + * The delegating router MUST include an IA_PD option, identifying any + * prefix(es) that the delegating router will delegate to the + * requesting router. + * [dhcpv6-opt-prefix-delegation-01 Section 10.2] + */ + if (!TAILQ_EMPTY(&optinfo->iapd_list)) { + int found = 0; + struct dhcp6_list conflist; + struct dhcp6_listval *iapd; + + TAILQ_INIT(&conflist); + + /* make a local copy of the configured prefixes */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->prefix_list)) { + dprintf(LOG_NOTICE, "%s" "failed to make local data", + FNAME); + goto fail; + } + + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + /* + * find an appropriate prefix for each IA_PD, + * removing the adopted prefixes from the list. + */ + if (make_ia(iapd, &conflist, &roptinfo.iapd_list, + client_conf, do_binding) > 0) + found = 1; + } + + dhcp6_clear_list(&conflist); + + if (!found) { + /* + * If the delegating router will not assign any + * prefixes to any IA_PDs in a subsequent Request from + * the requesting router, the delegating router MUST + * send an Advertise message to the requesting router + * that includes a Status Code option with code + * NoPrefixAvail. + * [dhcpv6-opt-prefix-delegation-01 Section 10.2] + */ + u_int16_t stcode = DH6OPT_STCODE_NOPREFIXAVAIL; + + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) + goto fail; } } @@ -659,7 +722,7 @@ server6_react_solicit(ifp, dh6, optinfo, from, fromlen) * server has been configured to respond with committed address * assignments and other resources, responds to the Solicit * with a Reply message. - * [dhcpv6-26 Section 17.2.1] + * [dhcpv6-28 Section 17.2.1] */ roptinfo.rapidcommit = 1; resptype = DH6_REPLY; @@ -688,7 +751,7 @@ server6_react_request(ifp, pi, dh6, optinfo, from, fromlen) struct dhcp6_optinfo roptinfo; struct host_conf *client_conf; - /* message validation according to Section 15.4 of dhcpv6-26 */ + /* message validation according to Section 15.4 of dhcpv6-28 */ /* the message must include a Server Identifier option */ if (optinfo->serverID.duid_len == 0) { @@ -729,16 +792,16 @@ server6_react_request(ifp, pi, dh6, optinfo, from, fromlen) * containing a Status Code option with value UseMulticast, a Server * Identifier option containing the server's DUID, the Client * Identifier option from the client message and no other options. - * [dhcpv6-26 18.2.1] + * [dhcpv6-28 18.2.1] * (Our current implementation never sends a unicast option.) */ if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { - int stcode = DH6OPT_STCODE_USEMULTICAST; + u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST; dprintf(LOG_INFO, "%s" "unexpected unicast message from %s", FNAME, addr2str(from)); - if (dhcp6_add_listval(&roptinfo.stcode_list, &stcode, - DHCP6_LISTVAL_NUM) == NULL) { + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a status code", FNAME); goto fail; @@ -759,12 +822,62 @@ server6_react_request(ifp, pi, dh6, optinfo, from, fromlen) * for the client. * (Note that our implementation does not assign addresses (nor will)). */ - /* prefixes */ - create_conflist(DHCP6_CONFINFO_PREFIX, - &optinfo->clientID, &roptinfo.prefix_list, - client_conf ? &client_conf->prefix_list : NULL, - &optinfo->prefix_list, - 1); + + /* + * When a delegating router receives a Request message from a + * requesting router that contains an IA_PD option, and the delegating + * router is authorized to delegate prefix(es) to the requesting + * router, the delegating router selects the prefix(es) to be delegated + * to the requesting router. + * [dhcpv6-opt-prefix-delegation-01 Section 11.2] + */ + if (!TAILQ_EMPTY(&optinfo->iapd_list)) { + struct dhcp6_list conflist; + struct dhcp6_listval *iapd; + + TAILQ_INIT(&conflist); + + /* make a local copy of the configured prefixes */ + if (client_conf && + dhcp6_copy_list(&conflist, &client_conf->prefix_list)) { + dprintf(LOG_NOTICE, "%s" "failed to make local data", + FNAME); + goto fail; + } + + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + /* + * Find an appropriate prefix for each IA_PD, + * removing the adopted prefixes from the list. + * The prefixes will be bound to the client. + */ + if (make_ia(iapd, &conflist, &roptinfo.iapd_list, + client_conf, 1) == 0) { + /* + * We could not find any prefixes for the IA. + * dhcpv6-28 specifies to include NoAddrAvail + * for the IA in the address configuration + * case (Section 18.2.1). We follow the same + * logic for prefix delegation as well. + */ + struct dhcp6_list *stcode_list; + + if (make_ia_stcode(DHCP6_LISTVAL_IAPD, + iapd->val_ia.iaid, + DH6OPT_STCODE_NOPREFIXAVAIL, + &roptinfo.iapd_list)) { + dprintf(LOG_NOTICE, "%s" + "failed to make an option list" + FNAME); + dhcp6_clear_list(&conflist); + goto fail; + } + } + } + + dhcp6_clear_list(&conflist); + } /* * If the Request message contained an Option Request option, the @@ -784,7 +897,7 @@ server6_react_request(ifp, pi, dh6, optinfo, from, fromlen) #endif /* - * Adds options to the Reply message for any other configuration + * Add options to the Reply message for any other configuration * information to be assigned to the client. */ /* DNS server */ @@ -816,11 +929,9 @@ server6_react_renew(ifp, pi, dh6, optinfo, from, fromlen) int fromlen; { struct dhcp6_optinfo roptinfo; - struct dhcp6_listval *lv; - struct dhcp6_binding *binding; - int add_success = 0; + struct dhcp6_listval *iapd; - /* message validation according to Section 15.6 of dhcpv6-26 */ + /* message validation according to Section 15.6 of dhcpv6-28 */ /* the message must include a Server Identifier option */ if (optinfo->serverID.duid_len == 0) { @@ -861,16 +972,16 @@ server6_react_renew(ifp, pi, dh6, optinfo, from, fromlen) * containing a status code option with value UseMulticast, a Server * Identifier option containing the server's DUID, the Client * Identifier option from the client message and no other options. - * [dhcpv6-26 18.2.3] + * [dhcpv6-28 18.2.3] * (Our current implementation never sends a unicast option.) */ if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) { - int stcode = DH6OPT_STCODE_USEMULTICAST; + u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST; dprintf(LOG_INFO, "%s" "unexpected unicast message from %s", FNAME, addr2str(from)); - if (dhcp6_add_listval(&roptinfo.stcode_list, &stcode, - DHCP6_LISTVAL_NUM) == NULL) { + if (dhcp6_add_listval(&roptinfo.stcode_list, + DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) { dprintf(LOG_ERR, "%s" "failed to add a status code", FNAME); goto fail; @@ -883,44 +994,19 @@ server6_react_renew(ifp, pi, dh6, optinfo, from, fromlen) /* * Locates the client's binding and verifies that the information * from the client matches the information stored for that client. + * (Note that our implementation does not assign addresses (nor will)). */ - /* prefixes */ - for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; - lv = TAILQ_NEXT(lv, link)) { - binding = find_binding(&optinfo->clientID, - DHCP6_CONFINFO_PREFIX, - &lv->val_prefix6); - if (binding == NULL) { - dprintf(LOG_INFO, "%s" "can't find a binding of prefix" - " %s/%d for %s", FNAME, - in6addr2str(&lv->val_prefix6.addr, 0), - lv->val_prefix6.plen, - duidstr(&optinfo->clientID)); - continue; /* XXX: is this okay? */ - } - - /* we always extend the requested binding. */ - update_binding(binding); - - /* include a Status Code option with value Success. */ - if (!add_success) { - int stcode = DH6OPT_STCODE_SUCCESS; - - if (dhcp6_add_listval(&roptinfo.stcode_list, - &stcode, DHCP6_LISTVAL_NUM) == NULL) { - dprintf(LOG_ERR, "%s" "failed to add a " - "status code", FNAME); - } - add_success = 1; - } - - /* add the prefix */ - if (dhcp6_add_listval(&roptinfo.prefix_list, binding->val, - DHCP6_LISTVAL_PREFIX6) == NULL) { - dprintf(LOG_ERR, "%s" "failed to add a renewed prefix", - FNAME); + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + if (make_binding_ia(iapd, &roptinfo.iapd_list, optinfo)) goto fail; - } + } + + /* add other configuration information */ + /* DNS server */ + if (dhcp6_copy_list(&roptinfo.dns_list, &dnslist)) { + dprintf(LOG_ERR, "%s" "failed to copy DNS list"); + goto fail; } (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, @@ -944,10 +1030,10 @@ server6_react_rebind(ifp, dh6, optinfo, from, fromlen) int fromlen; { struct dhcp6_optinfo roptinfo; - struct dhcp6_listval *lv; struct dhcp6_binding *binding; + struct dhcp6_listval *iapd; - /* message validation according to Section 15.7 of dhcpv6-26 */ + /* message validation according to Section 15.7 of dhcpv6-28 */ /* the message must include a Client Identifier option */ if (optinfo->clientID.duid_len == 0) { @@ -982,31 +1068,10 @@ server6_react_rebind(ifp, dh6, optinfo, from, fromlen) * Locates the client's binding and verifies that the information * from the client matches the information stored for that client. */ - /* prefixes */ - for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; - lv = TAILQ_NEXT(lv, link)) { - binding = find_binding(&optinfo->clientID, - DHCP6_CONFINFO_PREFIX, - &lv->val_prefix6); - if (binding == NULL) { - dprintf(LOG_INFO, "%s" "can't find a binding of prefix" - " %s/%d for %s", FNAME, - in6addr2str(&lv->val_prefix6.addr, 0), - lv->val_prefix6.plen, - duidstr(&optinfo->clientID)); - continue; /* XXX: is this okay? */ - } - - /* we always extend the requested binding. */ - update_binding(binding); - - /* add the prefix */ - if (dhcp6_add_listval(&roptinfo.prefix_list, binding->val, - DHCP6_LISTVAL_PREFIX6) == NULL) { - dprintf(LOG_ERR, "failed to add a rebound prefix", - FNAME); + for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd; + iapd = TAILQ_NEXT(iapd, link)) { + if (make_binding_ia(iapd, &roptinfo.iapd_list, optinfo)) goto fail; - } } /* add other configuration information */ @@ -1015,7 +1080,6 @@ server6_react_rebind(ifp, dh6, optinfo, from, fromlen) dprintf(LOG_ERR, "%s" "failed to copy DNS list"); goto fail; } - /* how about other prefixes? */ (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen, &roptinfo); @@ -1028,6 +1092,95 @@ server6_react_rebind(ifp, dh6, optinfo, from, fromlen) return (-1); } +static int +make_binding_ia(iapd, retlist, optinfo) + struct dhcp6_listval *iapd; + struct dhcp6_list *retlist; + struct dhcp6_optinfo *optinfo; +{ + struct dhcp6_binding *binding; + struct host_conf *client_conf; + + /* get per-host configuration for the client, if any. */ + if ((client_conf = find_hostconf(&optinfo->clientID))) { + dprintf(LOG_DEBUG, "%s" "found a host configuration named %s", + FNAME, client_conf->name); + } + + if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA, + iapd->type, iapd->val_ia.iaid)) == NULL) { + /* + * If the delegating router cannot find a binding for + * the requesting router's IA_PD the delegating router + * returns the IA_PD containing no prefixes with a + * Status Code option set to NoBinding in the Reply + * message. + * [dhcpv6-opt-prefix-delegation-01 Section 11.2] + * See also: [dhcpv6-28 Section 18.2.3] + */ + struct dhcp6_list *stcode_list; + + if (make_ia_stcode(iapd->type, iapd->val_ia.iaid, + DH6OPT_STCODE_NOBINDING, retlist)) { + dprintf(LOG_NOTICE, "%s" + "failed to make an option list" FNAME); + return (-1); + } + } else { /* we found a binding */ + struct dhcp6_list ialist; + struct dhcp6_listval *lv; + struct dhcp6_prefix prefix; + struct dhcp6_ia ia; + + TAILQ_INIT(&ialist); + update_binding(binding); + + /* see if each information to be renewed is still valid. */ + for (lv = TAILQ_FIRST(&iapd->sublist); lv; + lv = TAILQ_NEXT(lv, link)) { + switch (iapd->type) { + case DHCP6_LISTVAL_IAPD: + if (lv->type != DHCP6_LISTVAL_PREFIX6) + continue; + + prefix = lv->val_prefix6; + if (!dhcp6_find_listval(&binding->val_list, + DHCP6_LISTVAL_PREFIX6, &prefix, 0)) { + dprintf(LOG_DEBUG, "%s" + "%s/%d is not found in %s", FNAME, + in6addr2str(&prefix.addr, 0), + prefix.plen, bindingstr(binding)); + prefix.pltime = 0; + prefix.vltime = 0; + } + + if (dhcp6_add_listval(&ialist, + DHCP6_LISTVAL_PREFIX6, &prefix, NULL) + == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to " + "copy binding info", FNAME); + dhcp6_clear_list(&ialist); + return (-1); + } + } + } + + memset(&ia, 0, sizeof(ia)); + ia.iaid = binding->iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, &ialist, client_conf); + + if (dhcp6_add_listval(retlist, iapd->type, + &ia, &ialist) == NULL) { + dhcp6_clear_list(&ialist); + return (-1); + } + dhcp6_clear_list(&ialist); + } + + return (0); +} + static int server6_react_informreq(ifp, dh6, optinfo, from, fromlen) struct dhcp6_if *ifp; @@ -1135,94 +1288,284 @@ server6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo) } static int -create_conflist(type, clientid, ret_list, conf_list, req_list, do_binding) - dhcp6_conftype_t type; - struct duid *clientid; - struct dhcp6_list *ret_list, *conf_list, *req_list; +make_ia_stcode(iatype, iaid, stcode, retlist) + int iatype; + u_int16_t stcode; + u_int32_t iaid; + struct dhcp6_list *retlist; +{ + struct dhcp6_list stcode_list; + struct dhcp6_ia ia_empty; + + memset(&ia_empty, 0, sizeof(ia_empty)); + ia_empty.iaid = iaid; + + TAILQ_INIT(&stcode_list); + if (dhcp6_add_listval(&stcode_list, DHCP6_LISTVAL_STCODE, + &stcode, NULL) == NULL) { + dprintf(LOG_NOTICE, "%s" + "failed to make an option list", + FNAME); + return (-1); + } + + if (dhcp6_add_listval(retlist, iatype, + &ia_empty, &stcode_list) == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to make an option list", + FNAME); + dhcp6_clear_list(&stcode_list); + return (-1); + } + dhcp6_clear_list(&stcode_list); + + return (0); +} + +static int +make_ia(spec, conflist, retlist, client_conf, do_binding) + struct dhcp6_listval *spec; + struct dhcp6_list *conflist, *retlist; + struct host_conf *client_conf; int do_binding; { - struct dhcp6_listval *clv; struct dhcp6_binding *binding; - void *val; + struct dhcp6_list ialist; + struct dhcp6_listval *specia; + struct dhcp6_ia ia; + int found = 0; - if (conf_list == NULL) - return (0); + /* + * If we happen to have a binding already, update the binding and + * return it. Perhaps the request is being retransmitted. + */ + if ((binding = find_binding(&client_conf->duid, DHCP6_BINDING_IA, + spec->type, spec->val_ia.iaid)) != NULL) { + struct dhcp6_list *blist = &binding->val_list; + struct dhcp6_listval *bia, *v; - /* sanity check about type */ - switch(type) { - case DHCP6_CONFINFO_PREFIX: - break; - default: - dprintf(LOG_ERR, "%s" "unexpected configuration type(%d)", - FNAME, type); - exit(1); + dprintf(LOG_DEBUG, "%s" "we have a binding already: %s", + FNAME, bindingstr(binding)); + + update_binding(binding); + + memset(&ia, 0, sizeof(ia)); + ia.iaid = spec->val_ia.iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, blist, client_conf); + if (dhcp6_add_listval(retlist, spec->type, &ia, blist) + == NULL) { + dprintf(LOG_NOTICE, "%s" + "failed to copy binding info", FNAME); + return (0); + } + + /* remove bound values from the configuration */ + for (bia = TAILQ_FIRST(blist); bia; + bia = TAILQ_NEXT(bia, link)) { + if ((v = dhcp6_find_listval(conflist, + bia->type, &bia->uv, 0)) != NULL) { + found++; + TAILQ_REMOVE(conflist, v, link); + dhcp6_clear_listval(v); + } + } + + return (found); } - for (clv = TAILQ_FIRST(conf_list); clv; clv = TAILQ_NEXT(clv, link)) { - struct dhcp6_listval *dl; + /* + * trivial case: + * if the configuration is empty, we cannot make any IA. + */ + if (TAILQ_EMPTY(conflist)) + return (0); - /* - * If the client explicitly specified a list of option values, - * we only return those specified values (if authorized). - */ - if (req_list) { - switch(type) { - case DHCP6_CONFINFO_PREFIX: - if (dhcp6_find_listval(req_list, - &clv->val_prefix6, - DHCP6_LISTVAL_PREFIX6) == NULL) { - continue; - } - break; + TAILQ_INIT(&ialist); + + /* First, check if we can meet the client's requirement */ + for (specia = TAILQ_FIRST(&spec->sublist); specia; + specia = TAILQ_NEXT(specia, link)) { + /* try to find an IA that matches the spec best. */ + if (make_match_ia(specia, conflist, &ialist)) + found++; + } + if (found == 0) { + struct dhcp6_listval *v; + + /* use the first IA in the configuration list */ + v = TAILQ_FIRST(conflist); + if (dhcp6_add_listval(&ialist, v->type, &v->uv, NULL)) { + found = 1; + TAILQ_REMOVE(conflist, v, link); + dhcp6_clear_listval(v); + } + } + if (found) { + memset(&ia, 0, sizeof(ia)); + ia.iaid = spec->val_ia.iaid; + /* determine appropriate T1 and T2 */ + calc_ia_timo(&ia, &ialist, client_conf); + + /* make a binding for the set if necessary */ + if (found && do_binding) { + if (add_binding(&client_conf->duid, DHCP6_BINDING_IA, + spec->type, spec->val_ia.iaid, &ialist) == NULL) { + dprintf(LOG_NOTICE, "%s" "failed to make a" + " binding", FNAME); + found = 0; } } + if (found) { + /* make an IA for the set */ + if (dhcp6_add_listval(retlist, spec->type, + &ia, &ialist) == NULL) + found = 0; + } + dhcp6_clear_list(&ialist); + } + + return (found); +} + +static int +make_match_ia(spec, conflist, retlist) + struct dhcp6_listval *spec; + struct dhcp6_list *conflist, *retlist; +{ + struct dhcp6_listval *match; + int matched = 0; + + /* do we have the exact value specified? */ + match = dhcp6_find_listval(conflist, spec->type, &spec->uv, 0); + + /* if not, make further search specific to the IA type. */ + if (!match) { + switch (spec->type) { + case DHCP6_LISTVAL_PREFIX6: + match = dhcp6_find_listval(conflist, spec->type, + &spec->uv, MATCHLIST_PREFIXLEN); + break; + } + } + + /* + * if found, remove the matched entry from the configuration list + * and copy the value in the returned list. + */ + if (match) { + if (dhcp6_add_listval(retlist, match->type, + &match->uv, NULL)) { + matched = 1; + TAILQ_REMOVE(conflist, match, link); + dhcp6_clear_listval(match); + } + } + + return (matched); +} +static void +calc_ia_timo(ia, ialist, client_conf) + struct dhcp6_ia *ia; + struct dhcp6_list *ialist; /* this should not be empty */ + struct host_conf *client_conf; /* unused yet */ +{ + struct dhcp6_listval *iav; + u_int32_t base = DHCP6_DURATITION_INFINITE; + int iatype; + + iatype = TAILQ_FIRST(ialist)->type; + for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) { + if (iav->type != iatype) { + dprintf(LOG_ERR, "%s" + "assumption failure: IA list is not consistent", + FNAME); + exit (1); /* XXX */ + } + switch (iatype) { + case DHCP6_LISTVAL_PREFIX6: + if (base == DHCP6_DURATITION_INFINITE || + iav->val_prefix6.pltime < base) + base = iav->val_prefix6.pltime; + break; + } + } + + switch (iatype) { + case DHCP6_LISTVAL_PREFIX6: /* - * TODO: check if the requesting router is authorized. + * Configure the timeout parameters as recommended in + * Section 8 of dhcpv6-opt-prefix-delegation-01. + * We could also set the parameters to 0 if we let the client + * decide the renew timing (not implemented yet). */ - ; + if (base == DHCP6_DURATITION_INFINITE) { + ia->t1 = DHCP6_DURATITION_INFINITE; + ia->t2 = DHCP6_DURATITION_INFINITE; + } else { + ia->t1 = base / 2; + ia->t2 = (base * 4) / 5; + } + break; + } +} +static void +update_binding_duration(binding) + struct dhcp6_binding *binding; +{ + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *iav; + int duration = DHCP6_DURATITION_INFINITE; + u_int32_t past, lifetime, min_lifetime; + time_t now = time(NULL); + + min_lifetime = 0; + past = (u_int32_t)(now >= binding->updatetime ? + now - binding->updatetime : 0); + + switch (binding->type) { + case DHCP6_BINDING_IA: /* - * If we already have a binding for the prefix, the request - * is probably being retransmitted or the information is being - * renewed. Then just update the timer of the binding. - * Otherwise, create a binding for the prefix. + * Binding configuration is a list of IA parameters. + * Determine the minimum valid lifetime. */ - if (do_binding) { - if ((binding = find_binding(clientid, type, &clv->uv))) - update_binding(binding); - else if ((binding = add_binding(clientid, type, - &clv->uv)) == NULL) { - dprintf(LOG_ERR, "%s" "failed to create a " - "binding"); - continue; + for (iav = TAILQ_FIRST(ia_list); iav; + iav = TAILQ_NEXT(iav, link)) { + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + lifetime = iav->val_prefix6.vltime; + break; } - val = binding->val; - } else - val = (void *)&clv->uv; - - /* add the entry to the returned list */ - if ((dl = malloc(sizeof(*dl))) == NULL) { - dprintf(LOG_ERR, "%s" "failed to allocate " - "memory for prefix", FNAME); - continue; /* XXX: remove binding? */ - } - switch(type) { - case DHCP6_CONFINFO_PREFIX: - dl->val_prefix6 = *(struct dhcp6_prefix *)val; - break; + + if (min_lifetime == 0 || + (lifetime != DHCP6_DURATITION_INFINITE && + lifetime < min_lifetime)) + min_lifetime = lifetime; } - TAILQ_INSERT_TAIL(ret_list, dl, link); + if (past < min_lifetime) + duration = min_lifetime - past; + else + duration = 0; + + break; + default: + /* should be internal error. */ + dprintf(LOG_ERR, "%s" "unknown binding type (%d)", FNAME, + binding->type); + return; } - return (0); + binding->duration = duration; } static struct dhcp6_binding * -add_binding(clientid, type, val0) +add_binding(clientid, btype, iatype, iaid, val0) struct duid *clientid; - dhcp6_conftype_t type; + dhcp6_bindingtype_t btype; + int iatype; + u_int32_t iaid; void *val0; { struct dhcp6_binding *binding = NULL; @@ -1230,40 +1573,44 @@ add_binding(clientid, type, val0) char *val = NULL; if ((binding = malloc(sizeof(*binding))) == NULL) { - dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME); + dprintf(LOG_NOTICE, "%s" "failed to allocate memory", FNAME); return (NULL); } memset(binding, 0, sizeof(*binding)); - binding->type = type; + binding->type = btype; if (duidcpy(&binding->clientid, clientid)) { - dprintf(LOG_ERR, "%s" "failed to copy DUID"); + dprintf(LOG_NOTICE, "%s" "failed to copy DUID"); goto fail; } + binding->iatype = iatype; + binding->iaid = iaid; - switch(type) { - case DHCP6_CONFINFO_PREFIX: - duration = ((struct dhcp6_prefix *)val0)->duration; - if ((val = malloc(sizeof(struct dhcp6_prefix))) == NULL) { - dprintf(LOG_ERR, "%s" "failed to allocate memory for " - "prefix"); + /* construct configuration information for this binding */ + switch (btype) { + case DHCP6_BINDING_IA: + TAILQ_INIT(&binding->val_list); + if (dhcp6_copy_list(&binding->val_list, + (struct dhcp6_list *)val0)) { + dprintf(LOG_NOTICE, "%s" "failed to copy binding data", + FNAME); goto fail; } - memcpy(val, val0, sizeof(struct dhcp6_prefix)); - binding->val = val; break; default: - dprintf(LOG_ERR, "%s" "unexpected binding type(%d)", FNAME, - type); - exit(1); + dprintf(LOG_ERR, "%s" "unexpected binding type(%d)", + FNAME, btype); + goto fail; } - binding->duration = duration; - if (duration != DHCP6_DURATITION_INFINITE) { + /* calculate duration and start timer accordingly */ + binding->updatetime = time(NULL); + update_binding_duration(binding); + if (binding->duration != DHCP6_DURATITION_INFINITE) { struct timeval timo; binding->timer = dhcp6_add_timer(binding_timo, binding); if (binding->timer == NULL) { - dprintf(LOG_ERR, "%s" "failed to add timer", FNAME); + dprintf(LOG_NOTICE, "%s" "failed to add timer", FNAME); goto fail; } timo.tv_sec = (long)duration; @@ -1273,52 +1620,37 @@ add_binding(clientid, type, val0) TAILQ_INSERT_TAIL(&dhcp6_binding_head, binding, link); - dprintf(LOG_DEBUG, "%s" "add a new binding %s for %s", FNAME, - bindingstr(binding), duidstr(clientid)); + dprintf(LOG_DEBUG, "%s" "add a new binding %s", FNAME, + bindingstr(binding)); return (binding); fail: - if (binding) { - duidfree(&binding->clientid); - free(binding); - } - if (val) - free(val); + if (binding) + free_binding(binding); return (NULL); } static struct dhcp6_binding * -find_binding(clientid, type, val0) +find_binding(clientid, btype, iatype, iaid) struct duid *clientid; - dhcp6_conftype_t type; - void *val0; + dhcp6_bindingtype_t btype; + int iatype; + u_int32_t iaid; { struct dhcp6_binding *bp; struct dhcp6_prefix *pfx0, *pfx; for (bp = TAILQ_FIRST(&dhcp6_binding_head); bp; - bp = TAILQ_NEXT(bp, link)) { - if (bp->type != type || - duidcmp(&bp->clientid, clientid)) { + bp = TAILQ_NEXT(bp, link)) { + if (bp->type != btype || duidcmp(&bp->clientid, clientid)) continue; - } - switch(type) { - case DHCP6_CONFINFO_PREFIX: - pfx0 = (struct dhcp6_prefix *)val0; - pfx = (struct dhcp6_prefix *)bp->val; - if (pfx0->plen == pfx->plen && - IN6_ARE_ADDR_EQUAL(&pfx0->addr, &pfx->addr)) { - return (bp); - } - break; - default: - dprintf(LOG_ERR, - "%s" "unexpected binding type(%d)", FNAME, - type); - exit(1); - } + if (btype == DHCP6_BINDING_IA && + (bp->iatype != iatype || bp->iaid != iaid)) + continue; + + return (bp); } return (NULL); @@ -1330,6 +1662,13 @@ update_binding(binding) { struct timeval timo; + dprintf(LOG_DEBUG, "%s" "update binding %s for %s", FNAME, + bindingstr(binding), duidstr(&binding->clientid)); + + /* update timestamp and calculate new duration */ + binding->updatetime = time(NULL); + update_binding_duration(binding); + /* if the lease duration is infinite, there's nothing to do. */ if (binding->duration == DHCP6_DURATITION_INFINITE) return; @@ -1338,41 +1677,40 @@ update_binding(binding) timo.tv_sec = (long)binding->duration; timo.tv_usec = 0; dhcp6_set_timer(&timo, binding->timer); - - dprintf(LOG_DEBUG, "%s" "update a binding %s for %s", FNAME, - bindingstr(binding), duidstr(&binding->clientid)); } static void remove_binding(binding) struct dhcp6_binding *binding; { - void *val = binding->val; - - dprintf(LOG_DEBUG, "%s" "removing a binding %s for %s", FNAME, - bindingstr(binding), duidstr(&binding->clientid)); + dprintf(LOG_DEBUG, "%s" "remove a binding %s", FNAME, + bindingstr(binding)); if (binding->timer) dhcp6_remove_timer(&binding->timer); TAILQ_REMOVE(&dhcp6_binding_head, binding, link); - switch(binding->type) { - case DHCP6_CONFINFO_PREFIX: - dprintf(LOG_INFO, "%s" "remove prefix binding %s/%d for %s", - FNAME, - in6addr2str(&((struct dhcp6_prefix *)val)->addr, 0), - ((struct dhcp6_prefix *)val)->plen, - duidstr(&binding->clientid)); - free(binding->val); + free_binding(binding); +} + +static void +free_binding(binding) + struct dhcp6_binding *binding; +{ + duidfree(&binding->clientid); + + /* free configuration info in a type dependent manner. */ + switch (binding->type) { + case DHCP6_BINDING_IA: + dhcp6_clear_list(&binding->val_list); break; default: - dprintf(LOG_ERR, "%s" "unexpected binding type(%d)", FNAME, - binding->type); - exit(1); + dprintf(LOG_ERR, "%s" "unknown binding type %d", FNAME, + binding->type); + break; } - duidfree(&binding->clientid); free(binding); } @@ -1381,10 +1719,63 @@ binding_timo(arg) void *arg; { struct dhcp6_binding *binding = (struct dhcp6_binding *)arg; + struct dhcp6_list *ia_list = &binding->val_list; + struct dhcp6_listval *iav, *iav_next; + time_t now = time(NULL); + u_int32_t past, lifetime; + struct timeval timo; - remove_binding(binding); + past = (u_int32_t)(now >= binding->updatetime ? + now - binding->updatetime : 0); - return (NULL); + switch (binding->type) { + case DHCP6_BINDING_IA: + for (iav = TAILQ_FIRST(ia_list); iav; iav = iav_next) { + iav_next = TAILQ_NEXT(iav, link); + + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + lifetime = iav->val_prefix6.vltime; + break; + } + + if (lifetime != DHCP6_DURATITION_INFINITE && + lifetime <= past) { + dprintf(LOG_DEBUG, "%s" "bound prefix %s/%d" + " in %s has expired", FNAME, + in6addr2str(&iav->val_prefix6.addr, 0), + iav->val_prefix6.plen, + bindingstr(binding)); + TAILQ_REMOVE(ia_list, iav, link); + dhcp6_clear_listval(iav); + } + } + + /* If all IA parameters has expired, remove the binding. */ + if (TAILQ_EMPTY(ia_list)) { + remove_binding(binding); + return (NULL); + } + + break; + default: + dprintf(LOG_ERR, "%s" "unknown binding type %d", FNAME, + binding->type); + return (NULL); /* XXX */ + } + + update_binding_duration(binding); + + /* if the lease duration is infinite, there's nothing to do. */ + if (binding->duration == DHCP6_DURATITION_INFINITE) + return; + + /* reset the timer with the duration */ + timo.tv_sec = (long)binding->duration; + timo.tv_usec = 0; + dhcp6_set_timer(&timo, binding->timer); + + return (binding->timer); } static char * @@ -1393,19 +1784,25 @@ bindingstr(binding) { struct dhcp6_prefix *pfx; static char strbuf[LINE_MAX]; /* XXX: thread unsafe */ + char *iatype; + + switch (binding->type) { + case DHCP6_BINDING_IA: + switch (binding->iatype) { + case DHCP6_LISTVAL_IAPD: + iatype = "PD"; + break; + } - switch(binding->type) { - case DHCP6_CONFINFO_PREFIX: - pfx = (struct dhcp6_prefix *)binding->val; snprintf(strbuf, sizeof(strbuf), - "[prefix: %s/%d, duration=%ld]", - in6addr2str(&pfx->addr, 0), pfx->plen, - (unsigned long)binding->duration); + "[IA: duid=%s, type=%s, iaid=%lu, duration=%lu]", + duidstr(&binding->clientid), iatype, binding->iaid, + binding->duration); break; default: dprintf(LOG_ERR, "%s" "unexpected binding type(%d)", FNAME, binding->type); - exit(1); + return ("???"); } return (strbuf); diff --git a/kame/kame/dhcp6/prefixconf.c b/kame/kame/dhcp6/prefixconf.c index 05401b4cb4..9286a7dd64 100644 --- a/kame/kame/dhcp6/prefixconf.c +++ b/kame/kame/dhcp6/prefixconf.c @@ -1,4 +1,4 @@ -/* $KAME: prefixconf.c,v 1.10 2002/12/29 00:36:31 jinmei Exp $ */ +/* $KAME: prefixconf.c,v 1.11 2003/01/05 17:12:13 jinmei Exp $ */ /* * Copyright (C) 2002 WIDE Project. @@ -55,9 +55,32 @@ #include "config.h" #include "common.h" #include "timer.h" +#include "dhcp6c_ia.h" #include "prefixconf.h" -/* should be moved to a header file later */ +TAILQ_HEAD(siteprefix_list, siteprefix); +struct iactl_pd { + struct iactl common; + struct pifc_list *pifc_head; + struct siteprefix_list siteprefix_head; +}; +#define iacpd_ia common.iactl_ia +#define iacpd_callback common.callback +#define iacpd_isvalid common.isvalid +#define iacpd_duration common.duration +#define iacpd_renew_data common.renew_data +#define iacpd_rebind_data common.rebind_data +#define iacpd_cleanup common.cleanup + +struct siteprefix { + TAILQ_ENTRY (siteprefix) link; + + struct dhcp6_prefix prefix; + struct dhcp6_timer *timer; + struct iactl_pd *ctl; + TAILQ_HEAD(, dhcp6_ifprefix) ifprefix_list; /* interface prefixes */ +}; + struct dhcp6_ifprefix { TAILQ_ENTRY(dhcp6_ifprefix) plink; @@ -71,402 +94,327 @@ struct dhcp6_ifprefix { /* address assigned on the interface based on the prefix */ struct sockaddr_in6 ifaddr; }; -static TAILQ_HEAD(, dhcp6_siteprefix) siteprefix_listhead; typedef enum { IFADDRCONF_ADD, IFADDRCONF_REMOVE } ifaddrconf_cmd_t; +static struct siteprefix *find_siteprefix __P((struct siteprefix_list *, + struct dhcp6_prefix *)); +static void remove_siteprefix __P((struct siteprefix *)); +static int isvalid __P((struct iactl *)); +static u_int32_t duration __P((struct iactl *)); +static void cleanup __P((struct iactl *)); +static int renew_data __P((struct iactl *, struct dhcp6_ia *, + struct dhcp6_eventdata **, struct dhcp6_eventdata *)); +static void renew_data_free __P((struct dhcp6_eventdata *)); + +static struct dhcp6_timer *siteprefix_timo __P((void *)); + static int ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_ifprefix *)); -static struct dhcp6_siteprefix *find_siteprefix6 __P((struct dhcp6_prefix *)); -static struct dhcp6_timer *prefix6_timo __P((void *)); -static int add_ifprefix __P((struct dhcp6_siteprefix *, +static int add_ifprefix __P((struct siteprefix *, struct dhcp6_prefix *, struct prefix_ifconf *)); -static void prefix6_remove __P((struct dhcp6_siteprefix *)); -static int update __P((struct dhcp6_siteprefix *, struct dhcp6_prefix *, - struct duid *)); extern struct dhcp6_timer *client6_timo __P((void *)); extern void client6_send_renew __P((struct dhcp6_event *)); extern void client6_send_rebind __P((struct dhcp6_event *)); -void -prefix6_init() -{ - TAILQ_INIT(&siteprefix_listhead); -} - -void -prefix6_remove_all() +int +update_prefix(ia, pinfo, pifc, dhcpifp, ctlp, callback) + struct ia *ia; + struct dhcp6_prefix *pinfo; + struct pifc_list *pifc; + struct dhcp6_if *dhcpifp; + struct iactl **ctlp; + void (*callback)__P((struct ia *)); { - struct dhcp6_siteprefix *sp, *sp_next; + struct iactl_pd *iac_pd = (struct iactl_pd *)*ctlp; + struct siteprefix *sp; + struct prefix_ifconf *pif; + int spcreate = 0; + struct timeval timo; - for (sp = TAILQ_FIRST(&siteprefix_listhead); sp; sp = sp_next) { - sp_next = TAILQ_NEXT(sp, link); + /* + * Parameter check: reject prefixes where pltime is larger than vltime, + * though that the spec is silent on this point. + */ + if (pinfo->vltime != DHCP6_DURATITION_INFINITE && + (pinfo->pltime == DHCP6_DURATITION_INFINITE || + pinfo->pltime > pinfo->vltime)) { + dprintf(LOG_INFO, "%s" "invalid prefix %s/%d: " + "pltime (%lu) is larger than pltime (%lu)", + FNAME, in6addr2str(&pinfo->addr, 0), pinfo->plen, + pinfo->pltime, pinfo->vltime); + return (-1); + } - prefix6_remove(sp); + if (iac_pd == NULL) { + if ((iac_pd = malloc(sizeof(*iac_pd))) == NULL) { + dprintf(LOG_NOTICE, "%s" "memory allocation failed", + FNAME); + return (-1); + } + memset(iac_pd, 0, sizeof(*iac_pd)); + iac_pd->iacpd_ia = ia; + iac_pd->iacpd_callback = callback; + iac_pd->iacpd_isvalid = isvalid; + iac_pd->iacpd_duration = duration; + iac_pd->iacpd_cleanup = cleanup; + iac_pd->iacpd_renew_data = + iac_pd->iacpd_rebind_data = renew_data; + + iac_pd->pifc_head = pifc; + TAILQ_INIT(&iac_pd->siteprefix_head); + *ctlp = (struct iactl *)iac_pd; } -} -int -prefix6_add(ifp, prefix, serverid) - struct dhcp6_if *ifp; - struct dhcp6_prefix *prefix; - struct duid *serverid; -{ - struct prefix_ifconf *pif; - struct dhcp6_siteprefix *sp; + /* search for the given prefix, and make a new one if it fails */ + if ((sp = find_siteprefix(&iac_pd->siteprefix_head, pinfo)) == NULL) { + if ((sp = malloc(sizeof(*sp))) == NULL) { + dprintf(LOG_NOTICE, "%s" "memory allocation failed" + FNAME); + return (-1); + } + memset(sp, 0, sizeof(*sp)); + sp->prefix.addr = pinfo->addr; + sp->prefix.plen = pinfo->plen; + sp->ctl = iac_pd; + TAILQ_INIT(&sp->ifprefix_list); - dprintf(LOG_DEBUG, "%s" "try to add prefix %s/%d", FNAME, - in6addr2str(&prefix->addr, 0), prefix->plen); + TAILQ_INSERT_TAIL(&iac_pd->siteprefix_head, sp, link); - /* ignore meaningless prefix */ - if (prefix->duration == 0) { - dprintf(LOG_INFO, "%s" "zero duration for %s/%d", - in6addr2str(&prefix->addr, 0), prefix->plen); - return (0); + spcreate = 1; } - if ((sp = find_siteprefix6(prefix)) != NULL) { - dprintf(LOG_INFO, "%s" "duplicated delegated prefix: %s/%d", - FNAME, in6addr2str(&prefix->addr, 0), prefix->plen); - return (-1); - } + /* update the prefix according to pinfo */ + sp->prefix.pltime = pinfo->pltime; + sp->prefix.vltime = pinfo->vltime; + dprintf(LOG_DEBUG, "%s" "%s a prefix %s/%d pltime=%lu, vltime=%lu", + FNAME, spcreate ? "create" : "update", + in6addr2str(&pinfo->addr, 0), pinfo->plen, + pinfo->pltime, pinfo->vltime); + + /* update prefix interfaces if necessary */ + if (sp->prefix.vltime != 0 && spcreate) { + for (pif = TAILQ_FIRST(iac_pd->pifc_head); pif; + pif = TAILQ_NEXT(pif, link)) { + /* + * The requesting router MUST NOT assign any delegated + * prefixes or subnets from the delegated prefix(es) to + * the link through which it received the DHCP message + * from the delegating router. + * [dhcpv6-opt-prefix-delegation-01, Section 11.1] + */ + if (strcmp(pif->ifname, dhcpifp->ifname) == 0) { + dprintf(LOG_INFO, "%s" + "skip %s as a prefix interface", + FNAME, dhcpifp->ifname); + continue; + } - if ((sp = malloc(sizeof(*sp))) == NULL) { - dprintf(LOG_ERR, "%s" "failed to allocate memory" - " for a prefix", FNAME); - return (-1); - } - memset(sp, 0, sizeof(*sp)); - TAILQ_INIT(&sp->ifprefix_list); - sp->prefix = *prefix; - sp->ifp = ifp; - sp->state = PREFIX6S_ACTIVE; - if (duidcpy(&sp->serverid, serverid)) { - dprintf(LOG_ERR, "%s" "failed to copy server ID"); - goto fail; + add_ifprefix(sp, pinfo, pif); + } } - /* if a finite lease duration is specified, set up a timer. */ - if (sp->prefix.duration != DHCP6_DURATITION_INFINITE) { - struct timeval timo; - - if ((sp->timer = dhcp6_add_timer(prefix6_timo, sp)) == NULL) { - dprintf(LOG_ERR, "%s" "failed to add a timer for " - "prefix %s/%d", - in6addr2str(&prefix->addr, 0), prefix->plen); - goto fail; + /* + * If the new vltime is 0, this prefix immediatly expires. + * Otherwise, set up or update the associated timer. + */ + switch(sp->prefix.vltime) { + case 0: + remove_siteprefix(sp); + break; + case DHCP6_DURATITION_INFINITE: + if (sp->timer) + dhcp6_remove_timer(&sp->timer); + break; + default: + if (sp->timer == NULL) { + sp->timer = dhcp6_add_timer(siteprefix_timo, sp); + if (sp->timer == NULL) { + dprintf(LOG_NOTICE, + "%s" "failed to add prefix timer", FNAME); + remove_siteprefix(sp); /* XXX */ + return (-1); + } } - - timo.tv_sec = sp->prefix.duration >> 1; + /* update the timer */ + timo.tv_sec = sp->prefix.vltime; timo.tv_usec = 0; dhcp6_set_timer(&timo, sp->timer); + break; } - for (pif = prefix_ifconflist; pif; pif = pif->next) { - /* - * the requesting router MUST NOT assign any delegated - * prefixes or subnets from the delegated prefix(es) to the - * link through which it received the DHCP message from the - * delegating router. - * [dhcpv6-opt-prefix-delegation-01, Section 11.1] - */ - if (strcmp(pif->ifname, ifp->ifname) == 0) - continue; - - add_ifprefix(sp, prefix, pif); - } - - TAILQ_INSERT_TAIL(&siteprefix_listhead, sp, link); - return (0); +} - fail: - if (sp) { - duidfree(&sp->serverid); - free(sp); +static struct siteprefix * +find_siteprefix(head, prefix) + struct siteprefix_list *head; + struct dhcp6_prefix *prefix; +{ + struct siteprefix *sp; + + for (sp = TAILQ_FIRST(head); sp; sp = TAILQ_NEXT(sp, link)) { + if (IN6_ARE_ADDR_EQUAL(&sp->prefix.addr, &prefix->addr) && + sp->prefix.plen == prefix->plen) + return (sp); } - return (-1); + + return (NULL); } static void -prefix6_remove(sp) - struct dhcp6_siteprefix *sp; +remove_siteprefix(sp) + struct siteprefix *sp; { - struct dhcp6_ifprefix *ipf; + struct dhcp6_ifprefix *ip; - dprintf(LOG_DEBUG, "%s" "removing prefix %s/%d", FNAME, + dprintf(LOG_DEBUG, "%s" "remove a site prefix %s/%d", FNAME, in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen); - while ((ipf = TAILQ_FIRST(&sp->ifprefix_list)) != NULL) { - TAILQ_REMOVE(&sp->ifprefix_list, ipf, plink); - ifaddrconf(IFADDRCONF_REMOVE, ipf); - free(ipf); - } - - duidfree(&sp->serverid); - if (sp->timer) dhcp6_remove_timer(&sp->timer); - if (sp->evdata) { - TAILQ_REMOVE(&sp->evdata->event->data_list, sp->evdata, link); - free(sp->evdata); - sp->evdata = NULL; + /* remove all interface prefixes */ + while ((ip = TAILQ_FIRST(&sp->ifprefix_list)) != NULL) { + TAILQ_REMOVE(&sp->ifprefix_list, ip, plink); + ifaddrconf(IFADDRCONF_REMOVE, ip); + free(ip); } - TAILQ_REMOVE(&siteprefix_listhead, sp, link); - + TAILQ_REMOVE(&sp->ctl->siteprefix_head, sp, link); free(sp); } -int -prefix6_update(ev, prefix_list, serverid) - struct dhcp6_event *ev; - struct dhcp6_list *prefix_list; - struct duid *serverid; +static int +isvalid(iac) + struct iactl *iac; { - struct dhcp6_listval *lv; - struct dhcp6_eventdata *evd, *evd_next; - - /* add new prefixes */ - for (lv = TAILQ_FIRST(prefix_list); lv; lv = TAILQ_NEXT(lv, link)) { - if (find_siteprefix6(&lv->val_prefix6) != NULL) - continue; - - if (prefix6_add(ev->ifp, &lv->val_prefix6, serverid)) { - dprintf(LOG_INFO, "%s" "failed to add a new prefix"); - /* continue updating */ - } - } - - /* update existing prefixes */ - for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = evd_next) { - evd_next = TAILQ_NEXT(evd, link); + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; - if (evd->type != DHCP6_DATA_PREFIX) - continue; - - lv = dhcp6_find_listval(prefix_list, - &((struct dhcp6_siteprefix *)evd->data)->prefix, - DHCP6_LISTVAL_PREFIX6); - if (lv == NULL) - continue; - - TAILQ_REMOVE(&ev->data_list, evd, link); - ((struct dhcp6_siteprefix *)evd->data)->evdata = NULL; - - update((struct dhcp6_siteprefix *)evd->data, - &lv->val_prefix6, serverid); + if (TAILQ_EMPTY(&iac_pd->siteprefix_head)) + return (0); /* this IA is invalid */ + return (1); +} - free(evd); +static u_int32_t +duration(iac) + struct iactl *iac; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; + u_int32_t base = DHCP6_DURATITION_INFINITE; + + /* determine for the smallest pltime */ + for (sp = TAILQ_FIRST(&iac_pd->siteprefix_head); sp; + sp = TAILQ_NEXT(sp, link)) { + if (base == DHCP6_DURATITION_INFINITE || + sp->prefix.pltime < base) + base = sp->prefix.pltime; } - /* remove prefixes that were not updated */ - for (evd = TAILQ_FIRST(&ev->data_list); evd; evd = evd_next) { - evd_next = TAILQ_NEXT(evd, link); - - if (evd->type != DHCP6_DATA_PREFIX) - continue; - - TAILQ_REMOVE(&ev->data_list, evd, link); - ((struct dhcp6_siteprefix *)evd->data)->evdata = NULL; + return (base); +} - prefix6_remove((struct dhcp6_siteprefix *)evd->data); +static void +cleanup(iac) + struct iactl *iac; +{ + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; - free(evd); + while ((sp = TAILQ_FIRST(&iac_pd->siteprefix_head)) != NULL) { + TAILQ_REMOVE(&iac_pd->siteprefix_head, sp, link); + remove_siteprefix(sp); } - return (0); + free(iac); } static int -update(sp, prefix, serverid) - struct dhcp6_siteprefix *sp; - struct dhcp6_prefix *prefix; - struct duid *serverid; +renew_data(iac, iaparam, evdp, evd) + struct iactl *iac; + struct dhcp6_ia *iaparam; + struct dhcp6_eventdata **evdp, *evd; { - struct timeval timo; - - if (prefix->duration == DHCP6_DURATITION_INFINITE) { - dprintf(LOG_DEBUG, "%s" "update a prefix %s/%d " - "with infinite duration", FNAME, - in6addr2str(&prefix->addr, 0), prefix->plen, - prefix->duration); - } else { - dprintf(LOG_DEBUG, "%s" "update a prefix %s/%d " - "with duration %d", FNAME, - in6addr2str(&prefix->addr, 0), prefix->plen, - prefix->duration); - } - - sp->prefix.duration = prefix->duration; - - switch(sp->prefix.duration) { - case 0: - prefix6_remove(sp); - return (0); - case DHCP6_DURATITION_INFINITE: - if (sp->timer) - dhcp6_remove_timer(&sp->timer); - break; - default: - if (sp->timer == NULL) { - sp->timer = dhcp6_add_timer(prefix6_timo, sp); - if (sp->timer == NULL) { - dprintf(LOG_ERR, "%s" "failed to add prefix " - "timer", FNAME); - prefix6_remove(sp); /* XXX */ - return (-1); - } - } - /* update the timer */ - timo.tv_sec = sp->prefix.duration >> 1; - timo.tv_usec = 0; - - dhcp6_set_timer(&timo, sp->timer); - break; + struct iactl_pd *iac_pd = (struct iactl_pd *)iac; + struct siteprefix *sp; + struct dhcp6_list *ial = NULL, pl; + struct dhcp6_listval *v; + + TAILQ_INIT(&pl); + for (sp = TAILQ_FIRST(&iac_pd->siteprefix_head); sp; + sp = TAILQ_NEXT(sp, link)) { + if (dhcp6_add_listval(&pl, DHCP6_LISTVAL_PREFIX6, + &sp->prefix, NULL) == NULL) + goto fail; } - /* if we're rebinding the prefix, copy the new server ID. */ - if (sp->state == PREFIX6S_REBIND) { - if (duidcpy(&sp->serverid, serverid)) { - dprintf(LOG_ERR, "%s" "failed to copy server ID"); - prefix6_remove(sp); /* XXX */ - return (-1); - } - } + if ((ial = malloc(sizeof(*ial))) == NULL) + goto fail; + TAILQ_INIT(ial); + if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD, iaparam, &pl) == NULL) + goto fail; + dhcp6_clear_list(&pl); - sp->state = PREFIX6S_ACTIVE; + evd->type = DHCP6_EVDATA_IAPD; + evd->data = (void *)ial; + evd->privdata = (void *)evdp; + evd->destructor = renew_data_free; return (0); + + fail: + dhcp6_clear_list(&pl); + if (ial) + free(ial); + return (-1); } -static struct dhcp6_siteprefix * -find_siteprefix6(prefix) - struct dhcp6_prefix *prefix; +static void +renew_data_free(evd) + struct dhcp6_eventdata *evd; { - struct dhcp6_siteprefix *sp; + struct dhcp6_list *ial; - for (sp = TAILQ_FIRST(&siteprefix_listhead); sp; - sp = TAILQ_NEXT(sp, link)) { - if (sp->prefix.plen == prefix->plen && - IN6_ARE_ADDR_EQUAL(&sp->prefix.addr, &prefix->addr)) { - return (sp); - } + if (evd->type != DHCP6_EVDATA_IAPD) { + dprintf(LOG_ERR, "%s" "assumption failure", FNAME); + exit(1); } - return (NULL); + *(struct dhcp6_eventdata **)evd->privdata = NULL; + ial = (struct dhcp6_list *)evd->data; + dhcp6_clear_list(ial); + free(ial); } static struct dhcp6_timer * -prefix6_timo(arg) +siteprefix_timo(arg) void *arg; { - struct dhcp6_siteprefix *sp = (struct dhcp6_siteprefix *)arg; - struct dhcp6_event *ev; - struct dhcp6_eventdata *evd; - struct timeval timeo; - int dhcpstate; - double d; - - dprintf(LOG_DEBUG, "%s" "prefix timeout for %s/%d, state=%d", FNAME, - in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen, sp->state); - - /* cancel the current event for the prefix. */ - if (sp->evdata) { - TAILQ_REMOVE(&sp->evdata->event->data_list, sp->evdata, link); - free(sp->evdata); - sp->evdata = NULL; - } - - if (sp->state == PREFIX6S_REBIND) { - dprintf(LOG_INFO, "%s" "failed to rebind a prefix %s/%d", - FNAME, in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen); - prefix6_remove(sp); - return (NULL); - } - - switch(sp->state) { - case PREFIX6S_ACTIVE: - sp->state = PREFIX6S_RENEW; - dhcpstate = DHCP6S_RENEW; - d = sp->prefix.duration * 0.3; /* (0.8 - 0.5) * duration */ - timeo.tv_sec = (long)d; - timeo.tv_usec = 0; - break; - case PREFIX6S_RENEW: - sp->state = PREFIX6S_REBIND; - dhcpstate = DHCP6S_REBIND; - d = sp->prefix.duration * 0.2; /* (1.0 - 0.8) * duration */ - timeo.tv_sec = (long)d; - timeo.tv_usec = 0; - duidfree(&sp->serverid); - break; - default: - return (NULL); - } - dhcp6_set_timer(&timeo, sp->timer); + struct siteprefix *sp = (struct siteprefix *)arg; + struct ia *ia; + void (*callback)__P((struct ia *)); - if ((ev = dhcp6_create_event(sp->ifp, dhcpstate)) == NULL) { - dprintf(LOG_ERR, "%s" "failed to create a new event" - FNAME); - exit(1); /* XXX: should try to recover */ - } - if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { - dprintf(LOG_ERR, "%s" "failed to create a new event " - "timer", FNAME); - free(ev); - exit(1); /* XXX */ - } - if ((evd = malloc(sizeof(*evd))) == NULL) { - dprintf(LOG_ERR, "%s" "failed to create a new event " - "data", FNAME); - free(ev->timer); - free(ev); - exit(1); /* XXX */ - } - if (sp->state == PREFIX6S_RENEW) { - if (duidcpy(&ev->serverid, &sp->serverid)) { - dprintf(LOG_ERR, "%s" "failed to copy server ID", - FNAME); - free(ev->timer); - free(ev); - exit(1); /* XXX */ - } - } - memset(evd, 0, sizeof(*evd)); - evd->type = DHCP6_DATA_PREFIX; - evd->data = sp; - evd->event = ev; - TAILQ_INSERT_TAIL(&ev->data_list, evd, link); + dprintf(LOG_DEBUG, "%s" "prefix timeout for %s/%d", + FNAME, in6addr2str(&sp->prefix.addr, 0), sp->prefix.plen); - TAILQ_INSERT_TAIL(&sp->ifp->event_list, ev, link); + ia = sp->ctl->iacpd_ia; + callback = sp->ctl->iacpd_callback; - ev->timeouts = 0; - dhcp6_set_timeoparam(ev); - dhcp6_reset_timer(ev); + if (sp->timer) + dhcp6_remove_timer(&sp->timer); - sp->evdata = evd; + remove_siteprefix(sp); - switch(sp->state) { - case PREFIX6S_RENEW: - client6_send_renew(ev); - break; - case PREFIX6S_REBIND: - client6_send_rebind(ev); - break; - case PREFIX6S_ACTIVE: - /* what to do? */ - break; - } + (*callback)(ia); - return (sp->timer); + return (NULL); } static int add_ifprefix(siteprefix, prefix, pconf) - struct dhcp6_siteprefix *siteprefix; + struct siteprefix *siteprefix; struct dhcp6_prefix *prefix; struct prefix_ifconf *pconf; { @@ -477,7 +425,7 @@ add_ifprefix(siteprefix, prefix, pconf) int b, i; if ((ifpfx = malloc(sizeof(*ifpfx))) == NULL) { - dprintf(LOG_ERR, FNAME + dprintf(LOG_NOTICE, FNAME "failed to allocate memory for ifprefix"); return (-1); } @@ -493,7 +441,7 @@ add_ifprefix(siteprefix, prefix, pconf) * XXX: our current implementation assumes ifid len is a multiple of 8 */ if ((pconf->ifid_len % 8) != 0) { - dprintf(LOG_NOTICE, FNAME + dprintf(LOG_ERR, FNAME "assumption failure on the length of interface ID"); goto bad; } diff --git a/kame/kame/dhcp6/prefixconf.h b/kame/kame/dhcp6/prefixconf.h index 9d46f685d3..35e07f6ba9 100644 --- a/kame/kame/dhcp6/prefixconf.h +++ b/kame/kame/dhcp6/prefixconf.h @@ -1,4 +1,4 @@ -/* $KAME: prefixconf.h,v 1.3 2002/06/21 10:23:33 jinmei Exp $ */ +/* $KAME: prefixconf.h,v 1.4 2003/01/05 17:12:13 jinmei Exp $ */ /* * Copyright (C) 2002 WIDE Project. @@ -32,24 +32,9 @@ typedef enum { PREFIX6S_ACTIVE, PREFIX6S_RENEW, PREFIX6S_REBIND} prefix6state_t; -struct dhcp6_siteprefix { - TAILQ_ENTRY(dhcp6_siteprefix) link; /* link to next site prefix */ - - struct dhcp6_if *ifp; - struct dhcp6_prefix prefix; - struct duid serverid; - - prefix6state_t state; - struct dhcp6_timer *timer; - - struct dhcp6_eventdata *evdata; - - /* list of interface prefixes */ - TAILQ_HEAD(, dhcp6_ifprefix) ifprefix_list; -}; - -extern void prefix6_init __P((void)); -extern void prefix6_remove_all __P((void)); +extern int update_prefix __P((struct ia *, struct dhcp6_prefix *, + struct pifc_list *, struct dhcp6_if *, struct iactl **, + void (*)__P((struct ia *)))); extern int prefix6_add __P((struct dhcp6_if *, struct dhcp6_prefix *, struct duid *)); extern int prefix6_update __P((struct dhcp6_event *, struct dhcp6_list *,