1 /*	$KAME: addrconf.c,v 1.8 2005/09/16 11:30:13 suz Exp $	*/
2 
3 /*
4  * Copyright (C) 2002 WIDE Project.
5  * All rights reserved.
6  *
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.
18  *
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.
30  */
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>
36 
37 #include <net/if.h>
38 #ifdef __FreeBSD__
39 #include <net/if_var.h>
40 #endif
41 
42 #include <netinet/in.h>
43 
44 #ifdef __KAME__
45 #include <netinet6/in6_var.h>
46 #include <netinet6/nd6.h>
47 #endif
48 
49 #include <errno.h>
50 #include <syslog.h>
51 #include <string.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <unistd.h>
55 
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"
62 
63 TAILQ_HEAD(statefuladdr_list, statefuladdr);
64 struct iactl_na {
65 	struct iactl common;
66 	struct statefuladdr_list statefuladdr_head;
67 };
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
77 
78 struct statefuladdr {
79 	TAILQ_ENTRY (statefuladdr) link;
80 
81 	struct dhcp6_statefuladdr addr;
82 	time_t updatetime;
83 	struct dhcp6_timer *timer;
84 	struct iactl_na *ctl;
85 	struct dhcp6_if *dhcpif;
86 };
87 
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 *));
97 
98 static struct dhcp6_timer *addr_timo __P((void *));
99 
100 static int na_ifaddrconf __P((ifaddrconf_cmd_t, struct statefuladdr *));
101 
102 extern struct dhcp6_timer *client6_timo __P((void *));
103 
104 int
update_address(ia,addr,dhcpifp,ctlp,callback)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 *));
111 {
112 	struct iactl_na *iac_na = (struct iactl_na *)*ctlp;
113 	struct statefuladdr *sa;
114 	int sacreate = 0;
115 	struct timeval timo;
116 
117 	/*
118 	 * A client discards any addresses for which the preferred
119          * lifetime is greater than the valid lifetime.
120 	 * [RFC3315 22.6]
121 	 */
122 	if (addr->vltime != DHCP6_DURATION_INFINITE &&
123 	    (addr->pltime == DHCP6_DURATION_INFINITE ||
124 	    addr->pltime > addr->vltime)) {
125 		d_printf(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);
130 	}
131 
132 	if (iac_na == NULL) {
133 		if ((iac_na = malloc(sizeof(*iac_na))) == NULL) {
134 			d_printf(LOG_NOTICE, FNAME, "memory allocation failed");
135 			return (-1);
136 		}
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;
147 
148 		TAILQ_INIT(&iac_na->statefuladdr_head);
149 		*ctlp = (struct iactl *)iac_na;
150 	}
151 
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 			d_printf(LOG_NOTICE, FNAME, "memory allocation failed");
156 			return (-1);
157 		}
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;
163 	}
164 
165 	/* update the timestamp of update */
166 	sa->updatetime = time(NULL);
167 
168 	/* update the prefix according to addr */
169 	sa->addr.pltime = addr->pltime;
170 	sa->addr.vltime = addr->vltime;
171 	sa->dhcpif = dhcpifp;
172 	d_printf(LOG_DEBUG, FNAME, "%s an address %s pltime=%lu, vltime=%lu",
173 	    sacreate ? "create" : "update",
174 	    in6addr2str(&addr->addr, 0), addr->pltime, addr->vltime);
175 
176 	if (sa->addr.vltime != 0)
177 		if (na_ifaddrconf(IFADDRCONF_ADD, sa) < 0)
178 			return (-1);
179 
180 	/*
181 	 * If the new vltime is 0, this address immediately expires.
182 	 * Otherwise, set up or update the associated timer.
183 	 */
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 				d_printf(LOG_NOTICE, FNAME,
198 				    "failed to add stateful addr timer");
199 				remove_addr(sa); /* XXX */
200 				return (-1);
201 			}
202 		}
203 		/* update the timer */
204 		timo.tv_sec = sa->addr.vltime;
205 		timo.tv_usec = 0;
206 
207 		dhcp6_set_timer(&timo, sa->timer);
208 		break;
209 	}
210 
211 	return (0);
212 }
213 
214 static struct statefuladdr *
find_addr(head,addr)215 find_addr(head, addr)
216 	struct statefuladdr_list *head;
217 	struct dhcp6_statefuladdr *addr;
218 {
219 	struct statefuladdr *sa;
220 
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);
225 	}
226 
227 	return (NULL);
228 }
229 
230 static int
remove_addr(sa)231 remove_addr(sa)
232 	struct statefuladdr *sa;
233 {
234 	int ret;
235 
236 	d_printf(LOG_DEBUG, FNAME, "remove an address %s",
237 	    in6addr2str(&sa->addr.addr, 0));
238 
239 	if (sa->timer)
240 		dhcp6_remove_timer(&sa->timer);
241 
242 	TAILQ_REMOVE(&sa->ctl->statefuladdr_head, sa, link);
243 	ret = na_ifaddrconf(IFADDRCONF_REMOVE, sa);
244 	free(sa);
245 
246 	return (ret);
247 }
248 
249 static int
isvalid_addr(iac)250 isvalid_addr(iac)
251 	struct iactl *iac;
252 {
253 	struct iactl_na *iac_na = (struct iactl_na *)iac;
254 
255 	if (TAILQ_EMPTY(&iac_na->statefuladdr_head))
256 		return (0);	/* this IA is invalid */
257 	return (1);
258 }
259 
260 static u_int32_t
duration_addr(iac)261 duration_addr(iac)
262 	struct iactl *iac;
263 {
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;
268 
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;
277 
278 		if (base == DHCP6_DURATION_INFINITE || pltime < base)
279 			base = pltime;
280 	}
281 
282 	return (base);
283 }
284 
285 static void
cleanup_addr(iac)286 cleanup_addr(iac)
287 	struct iactl *iac;
288 {
289 	struct iactl_na *iac_na = (struct iactl_na *)iac;
290 	struct statefuladdr *sa;
291 
292 	while ((sa = TAILQ_FIRST(&iac_na->statefuladdr_head)) != NULL) {
293 		TAILQ_REMOVE(&iac_na->statefuladdr_head, sa, link);
294 		remove_addr(sa);
295 	}
296 
297 	free(iac);
298 }
299 
300 static int
renew_addr(iac,iaparam,evdp,evd)301 renew_addr(iac, iaparam, evdp, evd)
302 	struct iactl *iac;
303 	struct dhcp6_ia *iaparam;
304 	struct dhcp6_eventdata **evdp, *evd;
305 {
306 	struct iactl_na *iac_na = (struct iactl_na *)iac;
307 	struct statefuladdr *sa;
308 	struct dhcp6_list *ial = NULL, pl;
309 
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;
316 	}
317 
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);
324 
325 	evd->type = DHCP6_EVDATA_IANA;
326 	evd->data = (void *)ial;
327 	evd->privdata = (void *)evdp;
328 	evd->destructor = na_renew_data_free;
329 
330 	return (0);
331 
332   fail:
333 	dhcp6_clear_list(&pl);
334 	if (ial)
335 		free(ial);
336 	return (-1);
337 }
338 
339 static void
na_renew_data_free(evd)340 na_renew_data_free(evd)
341 	struct dhcp6_eventdata *evd;
342 {
343 	struct dhcp6_list *ial;
344 
345 	if (evd->type != DHCP6_EVDATA_IANA) {
346 		d_printf(LOG_ERR, FNAME, "assumption failure");
347 		exit(1);
348 	}
349 
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);
355 }
356 
357 static struct dhcp6_timer *
addr_timo(arg)358 addr_timo(arg)
359 	void *arg;
360 {
361 	struct statefuladdr *sa = (struct statefuladdr *)arg;
362 	struct ia *ia;
363 	void (*callback)__P((struct ia *));
364 
365 	d_printf(LOG_DEBUG, FNAME, "address timeout for %s",
366 	    in6addr2str(&sa->addr.addr, 0));
367 
368 	ia = sa->ctl->iacna_ia;
369 	callback = sa->ctl->iacna_callback;
370 
371 	if (sa->timer)
372 		dhcp6_remove_timer(&sa->timer);
373 
374 	remove_addr(sa);
375 
376 	(*callback)(ia);
377 
378 	return (NULL);
379 }
380 
381 static int
na_ifaddrconf(cmd,sa)382 na_ifaddrconf(cmd, sa)
383 	ifaddrconf_cmd_t cmd;
384 	struct statefuladdr *sa;
385 {
386 	struct dhcp6_statefuladdr *addr;
387 	struct sockaddr_in6 sin6;
388 
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;
396 
397 	return (ifaddrconf(cmd, sa->dhcpif->ifname, &sin6, 128,
398 	    addr->pltime, addr->vltime));
399 }
400