usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / dhcpv6 / addrconf.c
blobd98373f61b951a2fabe10973111e68f2087a2910
1 /* $KAME: addrconf.c,v 1.8 2005/09/16 11:30:13 suz Exp $ */
3 /*
4 * Copyright (C) 2002 WIDE Project.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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
29 * SUCH DAMAGE.
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/socket.h>
34 #include <sys/queue.h>
35 #include <sys/ioctl.h>
37 #include <net/if.h>
38 #ifdef __FreeBSD__
39 #include <net/if_var.h>
40 #endif
42 #include <netinet/in.h>
44 #ifdef __KAME__
45 #include <netinet6/in6_var.h>
46 #include <netinet6/nd6.h>
47 #endif
49 #include <errno.h>
50 #include <syslog.h>
51 #include <string.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <unistd.h>
56 #include "dhcp6.h"
57 #include "config.h"
58 #include "common.h"
59 #include "timer.h"
60 #include "dhcp6c_ia.h"
61 #include "prefixconf.h"
63 TAILQ_HEAD(statefuladdr_list, statefuladdr);
64 struct iactl_na {
65 struct iactl common;
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
78 struct statefuladdr {
79 TAILQ_ENTRY (statefuladdr) link;
81 struct dhcp6_statefuladdr addr;
82 time_t updatetime;
83 struct dhcp6_timer *timer;
84 struct iactl_na *ctl;
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)
106 struct ia *ia;
107 struct dhcp6_statefuladdr *addr;
108 struct dhcp6_if *dhcpifp;
109 struct iactl **ctlp;
110 void (*callback)__P((struct ia *));
112 struct iactl_na *iac_na = (struct iactl_na *)*ctlp;
113 struct statefuladdr *sa;
114 int sacreate = 0;
115 struct timeval timo;
118 * A client discards any addresses for which the preferred
119 * lifetime is greater than the valid lifetime.
120 * [RFC3315 22.6]
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);
129 return (-1);
132 if (iac_na == NULL) {
133 if ((iac_na = malloc(sizeof(*iac_na))) == NULL) {
134 dprintf(LOG_NOTICE, FNAME, "memory allocation failed");
135 return (-1);
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");
156 return (-1);
158 memset(sa, 0, sizeof(*sa));
159 sa->addr.addr = addr->addr;
160 sa->ctl = iac_na;
161 TAILQ_INSERT_TAIL(&iac_na->statefuladdr_head, sa, link);
162 sacreate = 1;
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)
177 if (na_ifaddrconf(IFADDRCONF_ADD, sa) < 0)
178 return (-1);
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) {
185 case 0:
186 if (remove_addr(sa) < 0)
187 return (-1);
188 break;
189 case DHCP6_DURATION_INFINITE:
190 if (sa->timer)
191 dhcp6_remove_timer(&sa->timer);
192 break;
193 default:
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 */
200 return (-1);
203 /* update the timer */
204 timo.tv_sec = sa->addr.vltime;
205 timo.tv_usec = 0;
207 dhcp6_set_timer(&timo, sa->timer);
208 break;
211 return (0);
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))
223 continue;
224 return (sa);
227 return (NULL);
230 static int
231 remove_addr(sa)
232 struct statefuladdr *sa;
234 int ret;
236 dprintf(LOG_DEBUG, FNAME, "remove an address %s",
237 in6addr2str(&sa->addr.addr, 0));
239 if (sa->timer)
240 dhcp6_remove_timer(&sa->timer);
242 TAILQ_REMOVE(&sa->ctl->statefuladdr_head, sa, link);
243 ret = na_ifaddrconf(IFADDRCONF_REMOVE, sa);
244 free(sa);
246 return (ret);
249 static int
250 isvalid_addr(iac)
251 struct iactl *iac;
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 */
257 return (1);
260 static u_int32_t
261 duration_addr(iac)
262 struct iactl *iac;
264 struct iactl_na *iac_na = (struct iactl_na *)iac;
265 struct statefuladdr *sa;
266 u_int32_t base = DHCP6_DURATION_INFINITE, pltime, passed;
267 time_t now;
269 /* Determine the smallest period until pltime expires. */
270 now = time(NULL);
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)
279 base = pltime;
282 return (base);
285 static void
286 cleanup_addr(iac)
287 struct iactl *iac;
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);
294 remove_addr(sa);
297 free(iac);
300 static int
301 renew_addr(iac, iaparam, evdp, evd)
302 struct iactl *iac;
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;
310 TAILQ_INIT(&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)
315 goto fail;
318 if ((ial = malloc(sizeof(*ial))) == NULL)
319 goto fail;
320 TAILQ_INIT(ial);
321 if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IANA, iaparam, &pl) == NULL)
322 goto fail;
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;
330 return (0);
332 fail:
333 dhcp6_clear_list(&pl);
334 if (ial)
335 free(ial);
336 return (-1);
339 static void
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");
347 exit(1);
350 if (evd->privdata)
351 *(struct dhcp6_eventdata **)evd->privdata = NULL;
352 ial = (struct dhcp6_list *)evd->data;
353 dhcp6_clear_list(ial);
354 free(ial);
357 static struct dhcp6_timer *
358 addr_timo(arg)
359 void *arg;
361 struct statefuladdr *sa = (struct statefuladdr *)arg;
362 struct ia *ia;
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;
371 if (sa->timer)
372 dhcp6_remove_timer(&sa->timer);
374 remove_addr(sa);
376 (*callback)(ia);
378 return (NULL);
381 static int
382 na_ifaddrconf(cmd, sa)
383 ifaddrconf_cmd_t cmd;
384 struct statefuladdr *sa;
386 struct dhcp6_statefuladdr *addr;
387 struct sockaddr_in6 sin6;
389 addr = &sa->addr;
390 memset(&sin6, 0, sizeof(sin6));
391 sin6.sin6_family = AF_INET6;
392 #ifdef HAVE_SA_LEN
393 sin6.sin6_len = sizeof(sin6);
394 #endif
395 sin6.sin6_addr = addr->addr;
397 return (ifaddrconf(cmd, sa->dhcpif->ifname, &sin6, 128,
398 addr->pltime, addr->vltime));