1 /* $KAME: dhcp6c_ia.c,v 1.33 2005/07/22 08:50:05 jinmei Exp $ */
4 * Copyright (C) 2003 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/socket.h>
36 #include <netinet/in.h>
48 #include "dhcp6c_ia.h"
49 #include "prefixconf.h"
52 typedef enum {IAS_ACTIVE
, IAS_RENEW
, IAS_REBIND
} iastate_t
;
57 /* back pointer to configuration */
60 /* common parameters of IA */
61 u_int32_t t1
; /* duration for renewal */
62 u_int32_t t2
; /* duration for rebind */
64 /* internal parameters for renewal/rebinding */
66 struct dhcp6_timer
*timer
;
67 struct dhcp6_eventdata
*evdata
;
69 /* DHCP related parameters */
70 struct dhcp6_if
*ifp
; /* DHCP interface */
71 struct duid serverid
; /* the server ID that provided this IA */
73 /* control information shared with each particular config routine */
76 /* authentication parameters for transaction with servers on this IA */
77 struct authparam
*authparam
;
80 static int update_authparam
__P((struct ia
*, struct authparam
*));
81 static void reestablish_ia
__P((struct ia
*));
82 static void callback
__P((struct ia
*));
83 static int release_ia
__P((struct ia
*));
84 static void remove_ia
__P((struct ia
*));
85 static struct ia
*get_ia
__P((iatype_t
, struct dhcp6_if
*, struct ia_conf
*,
86 struct dhcp6_listval
*, struct duid
*));
87 static struct ia
*find_ia
__P((struct ia_conf
*, iatype_t
, u_int32_t
));
88 static struct dhcp6_timer
*ia_timo
__P((void *));
90 static char *iastr
__P((iatype_t
));
91 static char *statestr
__P((iastate_t
));
94 update_ia(iatype
, ialist
, ifp
, serverid
, authparam
)
96 struct dhcp6_list
*ialist
;
98 struct duid
*serverid
;
99 struct authparam
*authparam
;
103 struct iapd_conf
*iapdc
;
104 struct iana_conf
*ianac
;
105 struct dhcp6_listval
*iav
, *siav
;
108 for (iav
= TAILQ_FIRST(ialist
); iav
; iav
= TAILQ_NEXT(iav
, link
)) {
109 /* if we're not interested in this IA, ignore it. */
110 if ((iac
= find_iaconf(&ifp
->iaconf_list
, iatype
,
111 iav
->val_ia
.iaid
)) == NULL
) {
115 /* validate parameters */
117 * If a client receives an IA_NA with T1 greater than T2, and
118 * both T1 and T2 are greater than 0, the client discards the
119 * IA_NA option and processes the remainder of the message as
120 * though the server had not included the invalid IA_NA option.
122 * We apply the same rule to IA_PD as well.
124 if (iav
->val_ia
.t2
!= 0 && iav
->val_ia
.t1
> iav
->val_ia
.t2
) {
125 dprintf(LOG_INFO
, FNAME
,
126 "invalid IA: T1(%lu) > T2(%lu)",
127 iav
->val_ia
.t1
, iav
->val_ia
.t2
);
131 /* locate the local IA or make a new one */
132 ia
= get_ia(iatype
, ifp
, iac
, iav
, serverid
);
134 dprintf(LOG_WARNING
, FNAME
, "failed to get an IA "
135 "type: %s, ID: %u", iastr(iac
->type
), iac
->iaid
);
139 /* update authentication parameters */
140 if (update_authparam(ia
, authparam
)) {
141 dprintf(LOG_WARNING
, FNAME
, "failed to update "
142 "authentication param for IA "
143 "type: %s, ID: %u", iastr(iac
->type
), iac
->iaid
);
148 /* update IA configuration information */
149 for (siav
= TAILQ_FIRST(&iav
->sublist
); siav
;
150 siav
= TAILQ_NEXT(siav
, link
)) {
151 switch (siav
->type
) {
152 case DHCP6_LISTVAL_PREFIX6
:
153 /* add or update the prefix */
154 iapdc
= (struct iapd_conf
*)iac
;
155 if (update_prefix(ia
, &siav
->val_prefix6
,
156 &iapdc
->iapd_pif_list
, ifp
, &ia
->ctl
,
158 dprintf(LOG_NOTICE
, FNAME
,
159 "failed to update a prefix %s/%d",
160 in6addr2str(&siav
->val_prefix6
.addr
, 0),
161 siav
->val_prefix6
.plen
);
164 case DHCP6_LISTVAL_STATEFULADDR6
:
165 ianac
= (struct iana_conf
*)iac
;
166 if (update_address(ia
, &siav
->val_statefuladdr6
,
167 ifp
, &ia
->ctl
, callback
)) {
168 dprintf(LOG_NOTICE
, FNAME
,
169 "failed to update an address %s",
170 in6addr2str(&siav
->val_statefuladdr6
.addr
, 0));
173 case DHCP6_LISTVAL_STCODE
:
174 dprintf(LOG_INFO
, FNAME
,
175 "status code for %s-%lu: %s",
176 iastr(iatype
), iav
->val_ia
.iaid
,
177 dhcp6_stcodestr(siav
->val_num16
));
178 if ((ia
->state
== IAS_RENEW
||
179 ia
->state
== IAS_REBIND
) &&
180 siav
->val_num16
== DH6OPT_STCODE_NOBINDING
) {
182 * For each IA in the original Renew or
183 * Rebind message, the client
184 * sends a Request message if the IA
185 * contained a Status Code option
186 * with the NoBinding status.
188 * XXX: what about the PD case?
190 dprintf(LOG_INFO
, FNAME
,
191 "receive NoBinding against "
192 "renew/rebind for %s-%lu",
193 iastr(ia
->conf
->type
),
200 dprintf(LOG_ERR
, FNAME
, "impossible case");
205 /* see if this IA is still valid. if not, remove it. */
206 if (ia
->ctl
== NULL
|| !(*ia
->ctl
->isvalid
)(ia
->ctl
)) {
207 dprintf(LOG_DEBUG
, FNAME
, "IA %s-%lu is invalidated",
208 iastr(ia
->conf
->type
), ia
->conf
->iaid
);
213 /* if T1 or T2 is 0, determine appropriate values locally. */
214 if (ia
->t1
== 0 || ia
->t2
== 0) {
217 if (ia
->ctl
&& ia
->ctl
->duration
)
218 duration
= (*ia
->ctl
->duration
)(ia
->ctl
);
220 duration
= 1800; /* 30min. XXX: no rationale */
223 if (duration
== DHCP6_DURATION_INFINITE
)
224 ia
->t1
= DHCP6_DURATION_INFINITE
;
226 ia
->t1
= duration
/ 2;
229 if (duration
== DHCP6_DURATION_INFINITE
)
230 ia
->t2
= DHCP6_DURATION_INFINITE
;
232 ia
->t2
= duration
* 4 / 5;
235 /* make sure T1 <= T2 */
237 ia
->t1
= ia
->t2
* 5 / 8;
239 dprintf(LOG_INFO
, FNAME
, "T1(%lu) and/or T2(%lu) "
240 "is locally determined", ia
->t1
, ia
->t2
);
244 * Be proactive for too-small timeout values. Note that
245 * the adjusted values may make some information expire
248 if (ia
->t2
< DHCP6_DURATION_MIN
) {
249 dprintf(LOG_INFO
, FNAME
, "T1 (%lu) or T2 (%lu) "
250 "is too small", ia
->t1
, ia
->t2
);
251 ia
->t2
= DHCP6_DURATION_MIN
;
252 ia
->t1
= ia
->t2
* 5 / 8;
253 dprintf(LOG_INFO
, "", " adjusted to %lu and %lu",
257 /* set up a timer for this IA. */
258 if (ia
->t1
== DHCP6_DURATION_INFINITE
) {
260 dhcp6_remove_timer(&ia
->timer
);
262 if (ia
->timer
== NULL
)
263 ia
->timer
= dhcp6_add_timer(ia_timo
, ia
);
264 if (ia
->timer
== NULL
) {
265 dprintf(LOG_ERR
, FNAME
,
266 "failed to add IA timer");
267 remove_ia(ia
); /* XXX */
270 timo
.tv_sec
= ia
->t1
;
272 dhcp6_set_timer(&timo
, ia
->timer
);
275 ia
->state
= IAS_ACTIVE
;
283 update_authparam(ia
, authparam
)
285 struct authparam
*authparam
;
287 if (authparam
== NULL
)
290 if (ia
->authparam
== NULL
) {
291 if ((ia
->authparam
= copy_authparam(authparam
)) == NULL
) {
292 dprintf(LOG_WARNING
, FNAME
,
293 "failed to copy authparam");
299 /* update the previous RD value and flags */
300 ia
->authparam
->prevrd
= authparam
->prevrd
;
301 ia
->authparam
->flags
= authparam
->flags
;
310 struct dhcp6_ia iaparam
;
311 struct dhcp6_event
*ev
;
312 struct dhcp6_eventdata
*evd
;
314 dprintf(LOG_DEBUG
, FNAME
, "re-establishing IA: %s-%lu",
315 iastr(ia
->conf
->type
), ia
->conf
->iaid
);
317 if (ia
->state
!= IAS_RENEW
&& ia
->state
!= IAS_REBIND
) {
318 dprintf(LOG_ERR
, FNAME
, "internal error (invalid IA status)");
322 /* cancel the current event for the prefix. */
324 TAILQ_REMOVE(&ia
->evdata
->event
->data_list
, ia
->evdata
, link
);
325 if (ia
->evdata
->destructor
)
326 ia
->evdata
->destructor(ia
->evdata
);
331 /* we don't need a timer for the IA (see comments in ia_timo()) */
333 dhcp6_remove_timer(&ia
->timer
);
335 if ((ev
= dhcp6_create_event(ia
->ifp
, DHCP6S_REQUEST
)) == NULL
) {
336 dprintf(LOG_NOTICE
, FNAME
, "failed to create a new event");
339 TAILQ_INSERT_TAIL(&ia
->ifp
->event_list
, ev
, link
);
341 if ((ev
->timer
= dhcp6_add_timer(client6_timo
, ev
)) == NULL
) {
342 dprintf(LOG_NOTICE
, FNAME
,
343 "failed to create a new event timer");
347 if ((evd
= malloc(sizeof(*evd
))) == NULL
) {
348 dprintf(LOG_NOTICE
, FNAME
,
349 "failed to create a new event data");
352 memset(evd
, 0, sizeof(*evd
));
354 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
356 if (duidcpy(&ev
->serverid
, &ia
->serverid
)) {
357 dprintf(LOG_NOTICE
, FNAME
, "failed to copy server ID");
361 iaparam
.iaid
= ia
->conf
->iaid
;
365 if (ia
->ctl
&& ia
->ctl
->reestablish_data
) {
366 if ((*ia
->ctl
->reestablish_data
)(ia
->ctl
, &iaparam
,
368 dprintf(LOG_NOTICE
, FNAME
,
369 "failed to make reestablish data");
374 if (ia
->authparam
!= NULL
) {
375 if ((ev
->authparam
= copy_authparam(ia
->authparam
)) == NULL
) {
376 dprintf(LOG_WARNING
, FNAME
,
377 "failed to copy authparam");
383 dhcp6_set_timeoparam(ev
);
384 dhcp6_reset_timer(ev
);
394 dhcp6_remove_event(ev
);
403 /* see if this IA is still valid. if not, remove it. */
404 if (ia
->ctl
== NULL
|| !(*ia
->ctl
->isvalid
)(ia
->ctl
)) {
405 dprintf(LOG_DEBUG
, FNAME
, "IA %s-%lu is invalidated",
406 iastr(ia
->conf
->type
), ia
->conf
->iaid
);
413 struct dhcp6_if
*ifp
;
416 struct ia
*ia
, *ia_next
;
418 for (iac
= TAILQ_FIRST(&ifp
->iaconf_list
); iac
;
419 iac
= TAILQ_NEXT(iac
, link
)) {
420 for (ia
= TAILQ_FIRST(&iac
->iadata
); ia
; ia
= ia_next
) {
421 ia_next
= TAILQ_NEXT(ia
, link
);
423 (void)release_ia(ia
);
426 * The client MUST stop using all of the addresses
427 * being released as soon as the client begins the
428 * Release message exchange process.
429 * [RFC3315 Section 18.1.6]
440 struct dhcp6_ia iaparam
;
441 struct dhcp6_event
*ev
;
442 struct dhcp6_eventdata
*evd
;
444 dprintf(LOG_DEBUG
, FNAME
, "release an IA: %s-%lu",
445 iastr(ia
->conf
->type
), ia
->conf
->iaid
);
447 if ((ev
= dhcp6_create_event(ia
->ifp
, DHCP6S_RELEASE
))
449 dprintf(LOG_NOTICE
, FNAME
, "failed to create a new event");
452 TAILQ_INSERT_TAIL(&ia
->ifp
->event_list
, ev
, link
);
455 if ((ev
->timer
= dhcp6_add_timer(client6_timo
, ev
)) == NULL
) {
456 dprintf(LOG_NOTICE
, FNAME
,
457 "failed to create a new event timer");
461 if (duidcpy(&ev
->serverid
, &ia
->serverid
)) {
462 dprintf(LOG_NOTICE
, FNAME
, "failed to copy server ID");
466 if ((evd
= malloc(sizeof(*evd
))) == NULL
) {
467 dprintf(LOG_NOTICE
, FNAME
,
468 "failed to create a new event data");
471 memset(evd
, 0, sizeof(*evd
));
472 iaparam
.iaid
= ia
->conf
->iaid
;
473 /* XXX: should we set T1/T2 to 0? spec is silent on this. */
477 if (ia
->ctl
&& ia
->ctl
->release_data
) {
478 if ((*ia
->ctl
->release_data
)(ia
->ctl
, &iaparam
, NULL
, evd
)) {
479 dprintf(LOG_NOTICE
, FNAME
,
480 "failed to make release data");
484 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
487 dhcp6_set_timeoparam(ev
);
488 dhcp6_reset_timer(ev
);
490 if (ia
->authparam
!= NULL
) {
491 if ((ev
->authparam
= copy_authparam(ia
->authparam
)) == NULL
) {
492 dprintf(LOG_WARNING
, FNAME
,
493 "failed to copy authparam");
504 dhcp6_remove_event(ev
);
513 struct ia_conf
*iac
= ia
->conf
;
514 struct dhcp6_if
*ifp
= ia
->ifp
;
516 dprintf(LOG_DEBUG
, FNAME
, "remove an IA: %s-%lu",
517 iastr(ia
->conf
->type
), ia
->conf
->iaid
);
519 TAILQ_REMOVE(&iac
->iadata
, ia
, link
);
521 duidfree(&ia
->serverid
);
524 dhcp6_remove_timer(&ia
->timer
);
527 TAILQ_REMOVE(&ia
->evdata
->event
->data_list
, ia
->evdata
, link
);
528 if (ia
->evdata
->destructor
)
529 ia
->evdata
->destructor(ia
->evdata
);
534 if (ia
->ctl
&& ia
->ctl
->cleanup
)
535 (*ia
->ctl
->cleanup
)(ia
->ctl
);
537 if (ia
->authparam
!= NULL
)
542 (void)client6_start(ifp
);
545 static struct dhcp6_timer
*
549 struct ia
*ia
= (struct ia
*)arg
;
550 struct dhcp6_ia iaparam
;
551 struct dhcp6_event
*ev
;
552 struct dhcp6_eventdata
*evd
;
556 dprintf(LOG_DEBUG
, FNAME
, "IA timeout for %s-%lu, state=%s",
557 iastr(ia
->conf
->type
), ia
->conf
->iaid
, statestr(ia
->state
));
559 /* cancel the current event for the prefix. */
561 TAILQ_REMOVE(&ia
->evdata
->event
->data_list
, ia
->evdata
, link
);
562 if (ia
->evdata
->destructor
)
563 ia
->evdata
->destructor(ia
->evdata
);
570 ia
->state
= IAS_RENEW
;
571 dhcpstate
= DHCP6S_RENEW
;
572 timo
.tv_sec
= ia
->t1
< ia
->t2
? ia
->t2
- ia
->t1
: 0;
574 dhcp6_set_timer(&timo
, ia
->timer
);
577 ia
->state
= IAS_REBIND
;
578 dhcpstate
= DHCP6S_REBIND
;
581 * We need keep DUID for sending Release in this state.
582 * But we don't need a timer for the IA. We'll just wait for a
583 * reply for the REBIND until all associated configuration
584 * parameters for this IA expire.
586 dhcp6_remove_timer(&ia
->timer
);
589 dprintf(LOG_ERR
, FNAME
, "invalid IA state (%d)",
591 return (NULL
); /* XXX */
594 if ((ev
= dhcp6_create_event(ia
->ifp
, dhcpstate
)) == NULL
) {
595 dprintf(LOG_NOTICE
, FNAME
, "failed to create a new event");
598 TAILQ_INSERT_TAIL(&ia
->ifp
->event_list
, ev
, link
);
600 if ((ev
->timer
= dhcp6_add_timer(client6_timo
, ev
)) == NULL
) {
601 dprintf(LOG_NOTICE
, FNAME
,
602 "failed to create a new event timer");
606 if ((evd
= malloc(sizeof(*evd
))) == NULL
) {
607 dprintf(LOG_NOTICE
, FNAME
,
608 "failed to create a new event data");
611 memset(evd
, 0, sizeof(*evd
));
613 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
615 if (ia
->state
== IAS_RENEW
) {
616 if (duidcpy(&ev
->serverid
, &ia
->serverid
)) {
617 dprintf(LOG_NOTICE
, FNAME
, "failed to copy server ID");
622 iaparam
.iaid
= ia
->conf
->iaid
;
627 if (ia
->ctl
&& ia
->ctl
->renew_data
) {
628 if ((*ia
->ctl
->renew_data
)(ia
->ctl
, &iaparam
,
630 dprintf(LOG_NOTICE
, FNAME
,
631 "failed to make renew data");
637 if (ia
->ctl
&& ia
->ctl
->rebind_data
) {
638 if ((*ia
->ctl
->rebind_data
)(ia
->ctl
, &iaparam
,
640 dprintf(LOG_NOTICE
, FNAME
,
641 "failed to make rebind data");
651 dhcp6_set_timeoparam(ev
);
652 dhcp6_reset_timer(ev
);
654 if (ia
->authparam
!= NULL
) {
655 if ((ev
->authparam
= copy_authparam(ia
->authparam
)) == NULL
) {
656 dprintf(LOG_WARNING
, FNAME
,
657 "failed to copy authparam");
678 dhcp6_remove_event(ev
);
684 get_ia(type
, ifp
, iac
, iaparam
, serverid
)
686 struct dhcp6_if
*ifp
;
688 struct dhcp6_listval
*iaparam
;
689 struct duid
*serverid
;
692 struct duid newserver
;
695 if (duidcpy(&newserver
, serverid
)) {
696 dprintf(LOG_NOTICE
, FNAME
, "failed to copy server ID");
700 if ((ia
= find_ia(iac
, type
, iaparam
->val_ia
.iaid
)) == NULL
) {
701 if ((ia
= malloc(sizeof(*ia
))) == NULL
) {
702 dprintf(LOG_NOTICE
, FNAME
, "memory allocation failed");
703 duidfree(&newserver
); /* XXX */
706 memset(ia
, 0, sizeof(*ia
));
707 ia
->state
= IAS_ACTIVE
;
709 TAILQ_INSERT_TAIL(&iac
->iadata
, ia
, link
);
714 duidfree(&ia
->serverid
);
716 ia
->t1
= iaparam
->val_ia
.t1
;
717 ia
->t2
= iaparam
->val_ia
.t2
;
719 ia
->serverid
= newserver
;
721 dprintf(LOG_DEBUG
, FNAME
, "%s an IA: %s-%lu",
722 create
? "make" : "update", iastr(type
), ia
->conf
->iaid
);
728 find_ia(iac
, type
, iaid
)
735 for (ia
= TAILQ_FIRST(&iac
->iadata
); ia
;
736 ia
= TAILQ_NEXT(ia
, link
)) {
737 if (ia
->conf
->type
== type
&& ia
->conf
->iaid
== iaid
)
754 return ("???"); /* should be a bug */
770 return "???"; /* should be a bug */