1 /* $KAME: addrconf.c,v 1.8 2005/09/16 11:30:13 suz Exp $ */
4 * Copyright (C) 2002 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>
33 #include <sys/socket.h>
34 #include <sys/queue.h>
35 #include <sys/ioctl.h>
39 #include <net/if_var.h>
42 #include <netinet/in.h>
45 #include <netinet6/in6_var.h>
46 #include <netinet6/nd6.h>
60 #include "dhcp6c_ia.h"
61 #include "prefixconf.h"
63 TAILQ_HEAD(statefuladdr_list
, statefuladdr
);
66 struct statefuladdr_list statefuladdr_head
;
68 #define iacna_ia common.iactl_ia
69 #define iacna_callback common.callback
70 #define iacna_isvalid common.isvalid
71 #define iacna_duration common.duration
72 #define iacna_renew_data common.renew_data
73 #define iacna_rebind_data common.rebind_data
74 #define iacna_reestablish_data common.reestablish_data
75 #define iacna_release_data common.release_data
76 #define iacna_cleanup common.cleanup
79 TAILQ_ENTRY (statefuladdr
) link
;
81 struct dhcp6_statefuladdr addr
;
83 struct dhcp6_timer
*timer
;
85 struct dhcp6_if
*dhcpif
;
88 static struct statefuladdr
*find_addr
__P((struct statefuladdr_list
*,
89 struct dhcp6_statefuladdr
*));
90 static int remove_addr
__P((struct statefuladdr
*));
91 static int isvalid_addr
__P((struct iactl
*));
92 static u_int32_t duration_addr
__P((struct iactl
*));
93 static void cleanup_addr
__P((struct iactl
*));
94 static int renew_addr
__P((struct iactl
*, struct dhcp6_ia
*,
95 struct dhcp6_eventdata
**, struct dhcp6_eventdata
*));
96 static void na_renew_data_free
__P((struct dhcp6_eventdata
*));
98 static struct dhcp6_timer
*addr_timo
__P((void *));
100 static int na_ifaddrconf
__P((ifaddrconf_cmd_t
, struct statefuladdr
*));
102 extern struct dhcp6_timer
*client6_timo
__P((void *));
105 update_address(ia
, addr
, dhcpifp
, ctlp
, callback
)
107 struct dhcp6_statefuladdr
*addr
;
108 struct dhcp6_if
*dhcpifp
;
110 void (*callback
)__P((struct ia
*));
112 struct iactl_na
*iac_na
= (struct iactl_na
*)*ctlp
;
113 struct statefuladdr
*sa
;
118 * A client discards any addresses for which the preferred
119 * lifetime is greater than the valid lifetime.
122 if (addr
->vltime
!= DHCP6_DURATION_INFINITE
&&
123 (addr
->pltime
== DHCP6_DURATION_INFINITE
||
124 addr
->pltime
> addr
->vltime
)) {
125 dprintf(LOG_INFO
, FNAME
, "invalid address %s: "
126 "pltime (%lu) is larger than vltime (%lu)",
127 in6addr2str(&addr
->addr
, 0),
128 addr
->pltime
, addr
->vltime
);
132 if (iac_na
== NULL
) {
133 if ((iac_na
= malloc(sizeof(*iac_na
))) == NULL
) {
134 dprintf(LOG_NOTICE
, FNAME
, "memory allocation failed");
137 memset(iac_na
, 0, sizeof(*iac_na
));
138 iac_na
->iacna_ia
= ia
;
139 iac_na
->iacna_callback
= callback
;
140 iac_na
->iacna_isvalid
= isvalid_addr
;
141 iac_na
->iacna_duration
= duration_addr
;
142 iac_na
->iacna_cleanup
= cleanup_addr
;
143 iac_na
->iacna_renew_data
=
144 iac_na
->iacna_rebind_data
=
145 iac_na
->iacna_release_data
=
146 iac_na
->iacna_reestablish_data
= renew_addr
;
148 TAILQ_INIT(&iac_na
->statefuladdr_head
);
149 *ctlp
= (struct iactl
*)iac_na
;
152 /* search for the given address, and make a new one if it fails */
153 if ((sa
= find_addr(&iac_na
->statefuladdr_head
, addr
)) == NULL
) {
154 if ((sa
= malloc(sizeof(*sa
))) == NULL
) {
155 dprintf(LOG_NOTICE
, FNAME
, "memory allocation failed");
158 memset(sa
, 0, sizeof(*sa
));
159 sa
->addr
.addr
= addr
->addr
;
161 TAILQ_INSERT_TAIL(&iac_na
->statefuladdr_head
, sa
, link
);
165 /* update the timestamp of update */
166 sa
->updatetime
= time(NULL
);
168 /* update the prefix according to addr */
169 sa
->addr
.pltime
= addr
->pltime
;
170 sa
->addr
.vltime
= addr
->vltime
;
171 sa
->dhcpif
= dhcpifp
;
172 dprintf(LOG_DEBUG
, FNAME
, "%s an address %s pltime=%lu, vltime=%lu",
173 sacreate
? "create" : "update",
174 in6addr2str(&addr
->addr
, 0), addr
->pltime
, addr
->vltime
);
176 if ((sa
->addr
.vltime
!= 0) && sacreate
)
177 if (na_ifaddrconf(IFADDRCONF_ADD
, sa
) < 0)
181 * If the new vltime is 0, this address immediately expires.
182 * Otherwise, set up or update the associated timer.
184 switch (sa
->addr
.vltime
) {
186 if (remove_addr(sa
) < 0)
189 case DHCP6_DURATION_INFINITE
:
191 dhcp6_remove_timer(&sa
->timer
);
194 if (sa
->timer
== NULL
) {
195 sa
->timer
= dhcp6_add_timer(addr_timo
, sa
);
196 if (sa
->timer
== NULL
) {
197 dprintf(LOG_NOTICE
, FNAME
,
198 "failed to add stateful addr timer");
199 remove_addr(sa
); /* XXX */
203 /* update the timer */
204 timo
.tv_sec
= sa
->addr
.vltime
;
207 dhcp6_set_timer(&timo
, sa
->timer
);
214 static struct statefuladdr
*
215 find_addr(head
, addr
)
216 struct statefuladdr_list
*head
;
217 struct dhcp6_statefuladdr
*addr
;
219 struct statefuladdr
*sa
;
221 for (sa
= TAILQ_FIRST(head
); sa
; sa
= TAILQ_NEXT(sa
, link
)) {
222 if (!IN6_ARE_ADDR_EQUAL(&sa
->addr
.addr
, &addr
->addr
))
232 struct statefuladdr
*sa
;
236 dprintf(LOG_DEBUG
, FNAME
, "remove an address %s",
237 in6addr2str(&sa
->addr
.addr
, 0));
240 dhcp6_remove_timer(&sa
->timer
);
242 TAILQ_REMOVE(&sa
->ctl
->statefuladdr_head
, sa
, link
);
243 ret
= na_ifaddrconf(IFADDRCONF_REMOVE
, sa
);
253 struct iactl_na
*iac_na
= (struct iactl_na
*)iac
;
255 if (TAILQ_EMPTY(&iac_na
->statefuladdr_head
))
256 return (0); /* this IA is invalid */
264 struct iactl_na
*iac_na
= (struct iactl_na
*)iac
;
265 struct statefuladdr
*sa
;
266 u_int32_t base
= DHCP6_DURATION_INFINITE
, pltime
, passed
;
269 /* Determine the smallest period until pltime expires. */
271 for (sa
= TAILQ_FIRST(&iac_na
->statefuladdr_head
); sa
;
272 sa
= TAILQ_NEXT(sa
, link
)) {
273 passed
= now
> sa
->updatetime
?
274 (u_int32_t
)(now
- sa
->updatetime
) : 0;
275 pltime
= sa
->addr
.pltime
> passed
?
276 sa
->addr
.pltime
- passed
: 0;
278 if (base
== DHCP6_DURATION_INFINITE
|| pltime
< base
)
289 struct iactl_na
*iac_na
= (struct iactl_na
*)iac
;
290 struct statefuladdr
*sa
;
292 while ((sa
= TAILQ_FIRST(&iac_na
->statefuladdr_head
)) != NULL
) {
293 TAILQ_REMOVE(&iac_na
->statefuladdr_head
, sa
, link
);
301 renew_addr(iac
, iaparam
, evdp
, evd
)
303 struct dhcp6_ia
*iaparam
;
304 struct dhcp6_eventdata
**evdp
, *evd
;
306 struct iactl_na
*iac_na
= (struct iactl_na
*)iac
;
307 struct statefuladdr
*sa
;
308 struct dhcp6_list
*ial
= NULL
, pl
;
311 for (sa
= TAILQ_FIRST(&iac_na
->statefuladdr_head
); sa
;
312 sa
= TAILQ_NEXT(sa
, link
)) {
313 if (dhcp6_add_listval(&pl
, DHCP6_LISTVAL_STATEFULADDR6
,
314 &sa
->addr
, NULL
) == NULL
)
318 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
321 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IANA
, iaparam
, &pl
) == NULL
)
323 dhcp6_clear_list(&pl
);
325 evd
->type
= DHCP6_EVDATA_IANA
;
326 evd
->data
= (void *)ial
;
327 evd
->privdata
= (void *)evdp
;
328 evd
->destructor
= na_renew_data_free
;
333 dhcp6_clear_list(&pl
);
340 na_renew_data_free(evd
)
341 struct dhcp6_eventdata
*evd
;
343 struct dhcp6_list
*ial
;
345 if (evd
->type
!= DHCP6_EVDATA_IANA
) {
346 dprintf(LOG_ERR
, FNAME
, "assumption failure");
351 *(struct dhcp6_eventdata
**)evd
->privdata
= NULL
;
352 ial
= (struct dhcp6_list
*)evd
->data
;
353 dhcp6_clear_list(ial
);
357 static struct dhcp6_timer
*
361 struct statefuladdr
*sa
= (struct statefuladdr
*)arg
;
363 void (*callback
)__P((struct ia
*));
365 dprintf(LOG_DEBUG
, FNAME
, "address timeout for %s",
366 in6addr2str(&sa
->addr
.addr
, 0));
368 ia
= sa
->ctl
->iacna_ia
;
369 callback
= sa
->ctl
->iacna_callback
;
372 dhcp6_remove_timer(&sa
->timer
);
382 na_ifaddrconf(cmd
, sa
)
383 ifaddrconf_cmd_t cmd
;
384 struct statefuladdr
*sa
;
386 struct dhcp6_statefuladdr
*addr
;
387 struct sockaddr_in6 sin6
;
390 memset(&sin6
, 0, sizeof(sin6
));
391 sin6
.sin6_family
= AF_INET6
;
393 sin6
.sin6_len
= sizeof(sin6
);
395 sin6
.sin6_addr
= addr
->addr
;
397 return (ifaddrconf(cmd
, sa
->dhcpif
->ifname
, &sin6
, 128,
398 addr
->pltime
, addr
->vltime
));