1/*	$Id: client6_addr.c,v 1.1.1.1 2006/12/04 00:45:21 Exp $	*/
2
3/*
4 * Copyright (C) International Business Machines  Corp., 2003
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
32/* Author: Shirley Ma, xma@us.ibm.com */
33
34#include <sys/types.h>
35#include <sys/time.h>
36#include <sys/socket.h>
37#include <sys/ioctl.h>
38
39#include <linux/ipv6.h>
40
41#include <net/if.h>
42#include <time.h>
43#include <errno.h>
44#include <syslog.h>
45#include <string.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <net/if_arp.h>
50
51#include "queue.h"
52#include "dhcp6.h"
53#include "config.h"
54#include "common.h"
55#include "timer.h"
56#include "lease.h"
57
58static int dhcp6_update_lease __P((struct dhcp6_addr *, struct dhcp6_lease *));
59static int dhcp6_add_lease __P((struct dhcp6_addr *));
60struct dhcp6_lease *dhcp6_find_lease __P((struct dhcp6_iaidaddr *,
61			struct dhcp6_addr *));
62int dhcp6_get_prefixlen __P((struct in6_addr *, struct dhcp6_if *));
63int client6_ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_addr *));
64u_int32_t get_min_preferlifetime __P((struct dhcp6_iaidaddr *));
65u_int32_t get_max_validlifetime __P((struct dhcp6_iaidaddr *));
66struct dhcp6_timer *dhcp6_iaidaddr_timo __P((void *));
67struct dhcp6_timer *dhcp6_lease_timo __P((void *));
68
69extern struct dhcp6_iaidaddr client6_iaidaddr;
70extern struct dhcp6_timer *client6_timo __P((void *));
71extern void client6_send __P((struct dhcp6_event *));
72extern void free_servers __P((struct dhcp6_if *));
73extern ssize_t gethwid __P((char *, int, const char *, u_int16_t *));
74
75extern int nlsock;
76extern FILE *client6_lease_file;
77extern struct dhcp6_iaidaddr client6_iaidaddr;
78extern struct dhcp6_list request_list;
79
80extern char* get_dhcpc_dev_name(void);
81
82void
83dhcp6_init_iaidaddr(void)
84{
85	memset(&client6_iaidaddr, 0, sizeof(client6_iaidaddr));
86	TAILQ_INIT(&client6_iaidaddr.lease_list);
87}
88
89static char callback_cmd[256] = "";
90int dhcp6c_dad_callback(void)
91{
92    /* Execute our callback function to restart other
93     * user-space apps, such as radvd, dhcp6s, etc
94     */
95    if (strlen(callback_cmd))
96    {
97        system(callback_cmd);
98        memset(callback_cmd, 0, sizeof(callback_cmd));
99    }
100    return 0;
101}
102
103int
104dhcp6_add_iaidaddr(struct dhcp6_optinfo *optinfo)
105{
106	struct dhcp6_listval *lv, *lv_next = NULL;
107	struct timeval timo;
108	struct dhcp6_lease *cl_lease;
109	double d;
110
111    char command[256], command2[256];
112    memset(command, 0, sizeof(command));
113
114	/* ignore IA with T1 > T2 */
115	if (client6_iaidaddr.client6_info.iaidinfo.renewtime >
116	    client6_iaidaddr.client6_info.iaidinfo.rebindtime) {
117		dprintf(LOG_INFO, " T1 time is greater than T2 time");
118		return (0);
119	}
120	memcpy(&client6_iaidaddr.client6_info.iaidinfo, &optinfo->iaidinfo,
121			sizeof(client6_iaidaddr.client6_info.iaidinfo));
122	client6_iaidaddr.client6_info.type = optinfo->type;
123	duidcpy(&client6_iaidaddr.client6_info.clientid, &optinfo->clientID);
124	if (duidcpy(&client6_iaidaddr.client6_info.serverid, &optinfo->serverID)) {
125		dprintf(LOG_ERR, "%s" "failed to copy server ID %s",
126			FNAME, duidstr(&optinfo->serverID));
127		return (-1);
128	}
129	/* add new address */
130	for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
131		lv_next = TAILQ_NEXT(lv, link);
132		if (lv->val_dhcp6addr.type != IAPD) {
133			lv->val_dhcp6addr.plen =
134				dhcp6_get_prefixlen(&lv->val_dhcp6addr.addr, dhcp6_if);
135			if (lv->val_dhcp6addr.plen == PREFIX_LEN_NOTINRA) {
136				dprintf(LOG_WARNING,
137					"assigned address %s prefix len is not in any RAs"
138					" prefix length using 64 bit instead",
139					in6addr2str(&lv->val_dhcp6addr.addr, 0));
140
141                sprintf(command, "dhcp6c_up %s %s %d ",
142                        get_dhcpc_dev_name(),
143                        in6addr2str(&lv->val_dhcp6addr.addr, 0),
144                        lv->val_dhcp6addr.plen);
145			}
146		}
147		if ((cl_lease = dhcp6_find_lease(&client6_iaidaddr,
148						&lv->val_dhcp6addr)) != NULL) {
149			dhcp6_update_lease(&lv->val_dhcp6addr, cl_lease);
150			continue;
151		}
152		if (dhcp6_add_lease(&lv->val_dhcp6addr)) {
153			dprintf(LOG_ERR, "%s" "failed to add a new addr lease %s",
154				FNAME, in6addr2str(&lv->val_dhcp6addr.addr, 0));
155			continue;
156		}
157	}
158	if (TAILQ_EMPTY(&client6_iaidaddr.lease_list))
159		return 0;
160
161	/* add new prefix (IAPD) */
162	for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; lv = lv_next) {
163		lv_next = TAILQ_NEXT(lv, link);
164		if (lv->val_dhcp6addr.type == IAPD) {
165            sprintf(command2, " %s %d &",
166                    in6addr2str(&lv->val_dhcp6addr.addr, 0),
167			        lv->val_dhcp6addr.plen);
168            if (!strlen(command))
169                sprintf(command, "dhcp6c_up %s ", get_dhcpc_dev_name());
170            strcat(command, command2);
171        }
172    }
173
174	/* set up renew T1, rebind T2 timer renew/rebind based on iaid */
175	/* Should we process IA_TA, IA_NA differently */
176	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0 ||
177	    client6_iaidaddr.client6_info.iaidinfo.renewtime >
178	    client6_iaidaddr.client6_info.iaidinfo.rebindtime) {
179		u_int32_t min_plifetime;
180		min_plifetime = get_min_preferlifetime(&client6_iaidaddr);
181		if (min_plifetime == DHCP6_DURATITION_INFINITE)
182			client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime;
183		else
184			client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime / 2;
185	}
186	if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0 ||
187		 client6_iaidaddr.client6_info.iaidinfo.renewtime >
188		 client6_iaidaddr.client6_info.iaidinfo.rebindtime) {
189		client6_iaidaddr.client6_info.iaidinfo.rebindtime =
190			get_min_preferlifetime(&client6_iaidaddr) * 4 / 5;
191	}
192	dprintf(LOG_INFO, "renew time %d, rebind time %d",
193		client6_iaidaddr.client6_info.iaidinfo.renewtime,
194		client6_iaidaddr.client6_info.iaidinfo.rebindtime);
195	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0)
196		return (0);
197	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == DHCP6_DURATITION_INFINITE) {
198		client6_iaidaddr.client6_info.iaidinfo.rebindtime = DHCP6_DURATITION_INFINITE;
199		return (0);
200	}
201	/* set up start date, and renew timer */
202	if ((client6_iaidaddr.timer =
203	    dhcp6_add_timer(dhcp6_iaidaddr_timo, &client6_iaidaddr)) == NULL) {
204		 dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
205			FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid);
206		 return (-1);
207	}
208	time(&client6_iaidaddr.start_date);
209	client6_iaidaddr.state = ACTIVE;
210	d = client6_iaidaddr.client6_info.iaidinfo.renewtime;
211	timo.tv_sec = (long)d;
212	timo.tv_usec = 0;
213	dhcp6_set_timer(&timo, client6_iaidaddr.timer);
214
215    /* Call our callback function to do something useful */
216    if (strlen(command))
217        strcpy(callback_cmd, command);
218        //system(command);
219
220	return (0);
221}
222
223int
224dhcp6_add_lease(addr)
225	struct dhcp6_addr *addr;
226{
227	struct dhcp6_lease *sp;
228	struct timeval timo;
229	double d;
230
231	dprintf(LOG_DEBUG, "%s" "try to add address %s", FNAME,
232		in6addr2str(&addr->addr, 0));
233
234	/* ignore meaningless address */
235	if (addr->status_code != DH6OPT_STCODE_SUCCESS &&
236			addr->status_code != DH6OPT_STCODE_UNDEFINE) {
237		dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME,
238			in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code));
239		return (0);
240	}
241	if (addr->validlifetime == 0 || addr->preferlifetime == 0 ||
242	    addr->preferlifetime > addr->validlifetime) {
243		dprintf(LOG_ERR, "%s" "invalid address life time for %s",
244			FNAME, in6addr2str(&addr->addr, 0));
245		return (0);
246	}
247	if ((sp = dhcp6_find_lease(&client6_iaidaddr, addr)) != NULL) {
248		dprintf(LOG_ERR, "%s" "duplicated address: %s",
249		    FNAME, in6addr2str(&addr->addr, 0));
250		return (-1);
251	}
252	if ((sp = (struct dhcp6_lease *)malloc(sizeof(*sp))) == NULL) {
253		dprintf(LOG_ERR, "%s" "failed to allocate memory"
254			" for a addr", FNAME);
255		return (-1);
256	}
257	memset(sp, 0, sizeof(*sp));
258	memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr));
259	sp->iaidaddr = &client6_iaidaddr;
260	time(&sp->start_date);
261	sp->state = ACTIVE;
262	if (write_lease(sp, client6_lease_file) != 0) {
263		dprintf(LOG_ERR, "%s" "failed to write a new lease address %s to lease file",
264			FNAME, in6addr2str(&sp->lease_addr.addr, 0));
265		if (sp->timer)
266			dhcp6_remove_timer(sp->timer);
267		free(sp);
268		return (-1);
269	}
270	if (sp->lease_addr.type == IAPD) {
271		dprintf(LOG_INFO, "request prefix is %s/%d",
272			in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen);
273	} else if (client6_ifaddrconf(IFADDRCONF_ADD, addr) != 0) {
274		dprintf(LOG_ERR, "%s" "adding address failed: %s",
275		    FNAME, in6addr2str(&addr->addr, 0));
276		if (sp->timer)
277			dhcp6_remove_timer(sp->timer);
278		free(sp);
279		return (-1);
280	}
281	TAILQ_INSERT_TAIL(&client6_iaidaddr.lease_list, sp, link);
282	/* for infinite lifetime don't do any timer */
283	if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE ||
284	    sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) {
285		dprintf(LOG_INFO, "%s" "infinity address life time for %s",
286			FNAME, in6addr2str(&addr->addr, 0));
287		return (0);
288	}
289	/* set up expired timer for lease*/
290	if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) {
291		dprintf(LOG_ERR, "%s" "failed to add a timer for lease %s",
292			FNAME, in6addr2str(&addr->addr, 0));
293		free(sp);
294		return (-1);
295	}
296	d = sp->lease_addr.preferlifetime;
297	timo.tv_sec = (long)d;
298	timo.tv_usec = 0;
299	dhcp6_set_timer(&timo, sp->timer);
300	return 0;
301}
302
303int
304dhcp6_remove_iaidaddr(struct dhcp6_iaidaddr *iaidaddr)
305{
306	struct dhcp6_lease *lv, *lv_next;
307	for (lv = TAILQ_FIRST(&iaidaddr->lease_list); lv; lv = lv_next) {
308		lv_next = TAILQ_NEXT(lv, link);
309		(void)dhcp6_remove_lease(lv);
310	}
311	/*
312	if (iaidaddr->client6_info.serverid.duid_id != NULL)
313		duidfree(&iaidaddr->client6_info.serverid);
314	 */
315	if (iaidaddr->timer)
316		dhcp6_remove_timer(iaidaddr->timer);
317	TAILQ_INIT(&iaidaddr->lease_list);
318	return 0;
319}
320
321int
322dhcp6_remove_lease(struct dhcp6_lease *sp)
323{
324	dprintf(LOG_DEBUG, "%s" "removing address %s", FNAME,
325		in6addr2str(&sp->lease_addr.addr, 0));
326	sp->state = INVALID;
327	if (write_lease(sp, client6_lease_file) != 0) {
328		dprintf(LOG_INFO, "%s"
329			"failed to write removed lease address %s to lease file",
330			FNAME, in6addr2str(&sp->lease_addr.addr, 0));
331		return (-1);
332	}
333	if (sp->lease_addr.type == IAPD) {
334		dprintf(LOG_INFO, "request prefix is %s/%d",
335			in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen);
336
337	} else if (client6_ifaddrconf(IFADDRCONF_REMOVE, &sp->lease_addr) != 0) {
338			dprintf(LOG_INFO, "%s" "removing address %s failed",
339		    		FNAME, in6addr2str(&sp->lease_addr.addr, 0));
340	}
341	/* remove expired timer for this lease. */
342	if (sp->timer)
343		dhcp6_remove_timer(sp->timer);
344	TAILQ_REMOVE(&client6_iaidaddr.lease_list, sp, link);
345	free(sp);
346	/* can't remove expired iaidaddr even there is no lease in this iaidaddr
347	 * since the rebind->solicit timer uses this iaidaddr
348	 * if(TAILQ_EMPTY(&client6_iaidaddr.lease_list))
349	 *	dhcp6_remove_iaidaddr();
350	 */
351
352	/* WNR3500L TD192:
353	 * Execute dhcp6c_down, so that LAN services and GUI
354	 * are restarted correctly.
355	 */
356	char command[256];
357	sprintf(command, "dhcp6c_down %s", get_dhcpc_dev_name());
358	system(command);
359
360	return 0;
361}
362
363int
364dhcp6_update_iaidaddr(struct dhcp6_optinfo *optinfo, int flag)
365{
366	struct dhcp6_listval *lv, *lv_next = NULL;
367	struct dhcp6_lease *cl, *cl_next;
368	struct timeval timo;
369	double d;
370    char command[256], command2[256];
371    memset(command, 0, sizeof(command));
372
373	if (client6_iaidaddr.client6_info.iaidinfo.renewtime >
374	    client6_iaidaddr.client6_info.iaidinfo.rebindtime) {
375		dprintf(LOG_INFO, " T1 time is greater than T2 time");
376		return (0);
377	}
378	if (flag == ADDR_REMOVE) {
379		for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
380			lv_next = TAILQ_NEXT(lv, link);
381			cl = dhcp6_find_lease(&client6_iaidaddr, &lv->val_dhcp6addr);
382			if (cl) {
383				/* remove leases */
384				dhcp6_remove_lease(cl);
385			}
386		}
387		return 0;
388	}
389	/* flag == ADDR_UPDATE */
390	for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
391		lv_next = TAILQ_NEXT(lv, link);
392		if (lv->val_dhcp6addr.type != IAPD) {
393			lv->val_dhcp6addr.plen =
394				dhcp6_get_prefixlen(&lv->val_dhcp6addr.addr, dhcp6_if);
395			if (lv->val_dhcp6addr.plen == PREFIX_LEN_NOTINRA) {
396				dprintf(LOG_WARNING, "assigned address %s is not in any RAs"
397					" prefix length using 64 bit instead",
398					in6addr2str(&lv->val_dhcp6addr.addr, 0));
399                sprintf(command, "dhcp6c_up %s %s %d ",
400                        get_dhcpc_dev_name(),
401                        in6addr2str(&lv->val_dhcp6addr.addr, 0),
402                        lv->val_dhcp6addr.plen);
403			}
404		}
405		if ((cl = dhcp6_find_lease(&client6_iaidaddr, &lv->val_dhcp6addr)) != NULL) {
406		/* update leases */
407			dhcp6_update_lease(&lv->val_dhcp6addr, cl);
408			continue;
409		}
410		/* need to add the new leases */
411		if (dhcp6_add_lease(&lv->val_dhcp6addr)) {
412			dprintf(LOG_INFO, "%s" "failed to add a new addr lease %s",
413				FNAME, in6addr2str(&lv->val_dhcp6addr.addr, 0));
414			continue;
415		}
416		continue;
417	}
418	/* remove leases that not on the updated list */
419	for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl; cl = cl_next) {
420			cl_next = TAILQ_NEXT(cl, link);
421		lv = dhcp6_find_listval(&optinfo->addr_list, &cl->lease_addr,
422			DHCP6_LISTVAL_DHCP6ADDR);
423		/* remove leases that not on the updated list */
424		if (lv == NULL)
425			dhcp6_remove_lease(cl);
426	}
427	/* update server id */
428	if (client6_iaidaddr.state == REBIND) {
429		if (duidcpy(&client6_iaidaddr.client6_info.serverid, &optinfo->serverID)) {
430			dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME);
431			return (-1);
432		}
433	}
434	if (TAILQ_EMPTY(&client6_iaidaddr.lease_list))
435		return (0);
436
437	/* add new prefix (IAPD) */
438	for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; lv = lv_next) {
439		lv_next = TAILQ_NEXT(lv, link);
440		if (lv->val_dhcp6addr.type == IAPD) {
441            sprintf(command2, " %s %d &",
442                    in6addr2str(&lv->val_dhcp6addr.addr, 0),
443			        lv->val_dhcp6addr.plen);
444            if (!strlen(command))
445                sprintf(command, "dhcp6c_up %s ", get_dhcpc_dev_name());
446            strcat(command, command2);
447        }
448    }
449
450	/* set up renew T1, rebind T2 timer renew/rebind based on iaid */
451	/* Should we process IA_TA, IA_NA differently */
452	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) {
453		u_int32_t min_plifetime;
454		min_plifetime = get_min_preferlifetime(&client6_iaidaddr);
455		if (min_plifetime == DHCP6_DURATITION_INFINITE)
456			client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime;
457		else
458			client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime / 2;
459	}
460	if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0) {
461		client6_iaidaddr.client6_info.iaidinfo.rebindtime =
462			get_min_preferlifetime(&client6_iaidaddr) * 4 / 5;
463	}
464	dprintf(LOG_INFO, "renew time %d, rebind time %d",
465		client6_iaidaddr.client6_info.iaidinfo.renewtime,
466		client6_iaidaddr.client6_info.iaidinfo.rebindtime);
467	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0)
468		return (0);
469	if (client6_iaidaddr.client6_info.iaidinfo.renewtime == DHCP6_DURATITION_INFINITE) {
470		client6_iaidaddr.client6_info.iaidinfo.rebindtime = DHCP6_DURATITION_INFINITE;
471		if (client6_iaidaddr.timer)
472			dhcp6_remove_timer(client6_iaidaddr.timer);
473		return (0);
474	}
475	/* update the start date and timer */
476	if (client6_iaidaddr.timer == NULL) {
477		if ((client6_iaidaddr.timer =
478		     dhcp6_add_timer(dhcp6_iaidaddr_timo, &client6_iaidaddr)) == NULL) {
479	 		dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
480				FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid);
481	 		return (-1);
482	    	}
483	}
484	time(&client6_iaidaddr.start_date);
485	client6_iaidaddr.state = ACTIVE;
486	d = client6_iaidaddr.client6_info.iaidinfo.renewtime;
487	timo.tv_sec = (long)d;
488	timo.tv_usec = 0;
489	dhcp6_set_timer(&timo, client6_iaidaddr.timer);
490
491    /* Call our callback function to do something useful */
492    if (strlen(command))
493        system(command);
494
495	return 0;
496}
497
498static int
499dhcp6_update_lease(struct dhcp6_addr *addr, struct dhcp6_lease *sp)
500{
501	struct timeval timo;
502	double d;
503
504	if (addr->status_code != DH6OPT_STCODE_SUCCESS &&
505	    addr->status_code != DH6OPT_STCODE_UNDEFINE) {
506		dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME,
507			in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code));
508		dhcp6_remove_lease(sp);
509		return (0);
510	}
511	/* remove leases with validlifetime == 0, and preferlifetime == 0 */
512	if (addr->validlifetime == 0 || addr->preferlifetime == 0 ||
513	    addr->preferlifetime > addr->validlifetime) {
514		dprintf(LOG_ERR, "%s" "invalid address life time for %s",
515			FNAME, in6addr2str(&addr->addr, 0));
516		dhcp6_remove_lease(sp);
517		return (0);
518	}
519	memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr));
520	sp->state = ACTIVE;
521	time(&sp->start_date);
522	if (write_lease(sp, client6_lease_file) != 0) {
523		dprintf(LOG_ERR, "%s"
524			"failed to write an updated lease address %s to lease file",
525			FNAME, in6addr2str(&sp->lease_addr.addr, 0));
526		return (-1);
527	}
528	if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE ||
529	    sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) {
530		dprintf(LOG_INFO, "%s" "infinity address life time for %s",
531			FNAME, in6addr2str(&addr->addr, 0));
532		if (sp->timer)
533			dhcp6_remove_timer(sp->timer);
534		return (0);
535	}
536	if (sp->timer == NULL) {
537		if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) {
538			dprintf(LOG_ERR, "%s" "failed to add a timer for lease %s",
539				FNAME, in6addr2str(&addr->addr, 0));
540			return (-1);
541		}
542	}
543	d = sp->lease_addr.preferlifetime;
544	timo.tv_sec = (long)d;
545	timo.tv_usec = 0;
546	dhcp6_set_timer(&timo, sp->timer);
547	return (0);
548}
549
550struct dhcp6_lease *
551dhcp6_find_lease(struct dhcp6_iaidaddr *iaidaddr,
552		 struct dhcp6_addr *ifaddr)
553{
554	struct dhcp6_lease *sp;
555	for (sp = TAILQ_FIRST(&iaidaddr->lease_list); sp;
556	     sp = TAILQ_NEXT(sp, link)) {
557		/* sp->lease_addr.plen == ifaddr->plen */
558		dprintf(LOG_DEBUG, "%s" "get address is %s/%d ", FNAME,
559			in6addr2str(&ifaddr->addr, 0), ifaddr->plen);
560		dprintf(LOG_DEBUG, "%s" "lease address is %s/%d ", FNAME,
561			in6addr2str(&sp->lease_addr.addr, 0), ifaddr->plen);
562		if (IN6_ARE_ADDR_EQUAL(&sp->lease_addr.addr, &ifaddr->addr)) {
563			if (sp->lease_addr.type == IAPD) {
564				if (sp->lease_addr.plen == ifaddr->plen)
565					return (sp);
566			} else if (sp->lease_addr.type == IANA ||
567				   sp->lease_addr.type == IATA)
568				return (sp);
569		}
570	}
571	return (NULL);
572}
573
574struct dhcp6_timer *
575dhcp6_iaidaddr_timo(void *arg)
576{
577	struct dhcp6_iaidaddr *sp = (struct dhcp6_iaidaddr *)arg;
578	struct dhcp6_event *ev;
579	struct timeval timeo;
580	int dhcpstate;
581	double d = 0;
582
583	dprintf(LOG_DEBUG, "client6_iaidaddr timeout for %d, state=%d",
584		client6_iaidaddr.client6_info.iaidinfo.iaid, sp->state);
585
586	dhcp6_clear_list(&request_list);
587	TAILQ_INIT(&request_list);
588	/* ToDo: what kind of opiton Request value, client would like to pass? */
589	switch(sp->state) {
590	case ACTIVE:
591		sp->state = RENEW;
592		dhcpstate = DHCP6S_RENEW;
593		d = sp->client6_info.iaidinfo.rebindtime - sp->client6_info.iaidinfo.renewtime;
594		timeo.tv_sec = (long)d;
595		timeo.tv_usec = 0;
596		break;
597	case RENEW:
598		sp->state = REBIND;
599		dhcpstate = DHCP6S_REBIND;
600		d = get_max_validlifetime(&client6_iaidaddr) -
601				sp->client6_info.iaidinfo.rebindtime;
602		timeo.tv_sec = (long)d;
603		timeo.tv_usec = 0;
604		if (sp->client6_info.serverid.duid_id != NULL)
605			duidfree(&sp->client6_info.serverid);
606		break;
607	case REBIND:
608		dprintf(LOG_INFO, "%s" "failed to rebind a client6_iaidaddr %d"
609		    " go to solicit and request new ipv6 addresses",
610		    FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid);
611		sp->state = INVALID;
612		dhcpstate = DHCP6S_SOLICIT;
613		free_servers(sp->ifp);
614		break;
615	default:
616		return (NULL);
617	}
618	if ((ev = dhcp6_create_event(sp->ifp, dhcpstate)) == NULL) {
619		dprintf(LOG_ERR, "%s" "failed to create a new event",
620		    FNAME);
621		return (NULL);
622	}
623	switch(sp->state) {
624	case RENEW:
625		if (duidcpy(&ev->serverid, &sp->client6_info.serverid)) {
626			dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME);
627			free(ev);
628			return (NULL);
629		}
630	case REBIND:
631		/* BUG: d not set! */
632		ev->max_retrans_dur = d;
633		break;
634	default:
635		break;
636	}
637	if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) {
638		dprintf(LOG_ERR, "%s" "failed to create a new event timer", FNAME);
639		if (sp->state == RENEW)
640			duidfree(&ev->serverid);
641		free(ev);
642		return (NULL);
643	}
644	TAILQ_INSERT_TAIL(&sp->ifp->event_list, ev, link);
645	if (sp->state != INVALID) {
646		struct dhcp6_lease *cl;
647		/* create an address list for renew and rebind */
648		for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl;
649			cl = TAILQ_NEXT(cl, link)) {
650			struct dhcp6_listval *lv;
651			/* IA_NA address */
652			if ((lv = malloc(sizeof(*lv))) == NULL) {
653				dprintf(LOG_ERR, "%s"
654				"failed to allocate memory for an ipv6 addr", FNAME);
655				if (sp->state == RENEW)
656					duidfree(&ev->serverid);
657				free(ev->timer);
658				free(ev);
659		 		return (NULL);
660			}
661			memcpy(&lv->val_dhcp6addr, &cl->lease_addr,
662					sizeof(lv->val_dhcp6addr));
663			lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
664			TAILQ_INSERT_TAIL(&request_list, lv, link);
665		}
666		dhcp6_set_timer(&timeo, sp->timer);
667	} else {
668		dhcp6_remove_iaidaddr(&client6_iaidaddr);
669		/* remove event data for that event */
670		sp->timer = NULL;
671	}
672	ev->timeouts = 0;
673	dhcp6_set_timeoparam(ev);
674	dhcp6_reset_timer(ev);
675	client6_send(ev);
676	return (sp->timer);
677}
678
679
680struct dhcp6_timer *
681dhcp6_lease_timo(void *arg)
682{
683	struct dhcp6_lease *sp = (struct dhcp6_lease *)arg;
684	struct timeval timeo;
685	double d;
686
687	dprintf(LOG_DEBUG, "%s" "lease timeout for %s, state=%d", FNAME,
688		in6addr2str(&sp->lease_addr.addr, 0), sp->state);
689	/* cancel the current event for this lease */
690	if (sp->state == INVALID) {
691		dprintf(LOG_INFO, "%s" "failed to remove an addr %s",
692		    FNAME, in6addr2str(&sp->lease_addr.addr, 0));
693		dhcp6_remove_lease(sp);
694		return (NULL);
695	}
696	switch(sp->state) {
697	case ACTIVE:
698		sp->state = EXPIRED;
699		d = sp->lease_addr.validlifetime - sp->lease_addr.preferlifetime;
700		timeo.tv_sec = (long)d;
701		timeo.tv_usec = 0;
702		dhcp6_set_timer(&timeo, sp->timer);
703		break;
704	case EXPIRED:
705		sp->state = INVALID;
706		dhcp6_remove_lease(sp);
707	default:
708		return (NULL);
709	}
710	return (sp->timer);
711}
712
713int
714client6_ifaddrconf(ifaddrconf_cmd_t cmd, struct dhcp6_addr *ifaddr)
715{
716	struct in6_ifreq req;
717	struct dhcp6_if *ifp = client6_iaidaddr.ifp;
718	unsigned long ioctl_cmd;
719	char *cmdstr;
720	int s, errno;
721
722	switch(cmd) {
723	case IFADDRCONF_ADD:
724		cmdstr = "add";
725		ioctl_cmd = SIOCSIFADDR;
726		break;
727	case IFADDRCONF_REMOVE:
728		cmdstr = "remove";
729		ioctl_cmd = SIOCDIFADDR;
730		break;
731	default:
732		return (-1);
733	}
734
735	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
736		dprintf(LOG_ERR, "%s" "can't open a temporary socket: %s",
737			FNAME, strerror(errno));
738		return (-1);
739	}
740	memset(&req, 0, sizeof(req));
741	req.ifr6_ifindex = if_nametoindex(ifp->ifname);
742	memcpy(&req.ifr6_addr, &ifaddr->addr, sizeof(req.ifr6_addr));
743
744	req.ifr6_prefixlen = ifaddr->plen;
745
746	if (ioctl(s, ioctl_cmd, &req) && errno != EEXIST) {
747		dprintf(LOG_NOTICE, "%s" "failed to %s an address on %s: %s",
748		    FNAME, cmdstr, ifp->ifname, strerror(errno));
749		close(s);
750		return (-1);
751	}
752
753	dprintf(LOG_DEBUG, "%s" "%s an address %s on %s", FNAME, cmdstr,
754	    in6addr2str(&ifaddr->addr, 0), ifp->ifname);
755	close(s);
756	return (0);
757}
758
759
760int
761get_iaid(const char *ifname, const struct iaid_table *iaidtab, int num_device)
762{
763	struct hardware hdaddr;
764	struct iaid_table *temp = (struct iaid_table *)iaidtab;
765	int i;
766	hdaddr.len = gethwid(hdaddr.data, 6, ifname, &hdaddr.type);
767	for (i = 0; i < num_device; i++, temp++) {
768		if (!memcmp(temp->hwaddr.data, hdaddr.data, temp->hwaddr.len)
769		    && hdaddr.len == temp->hwaddr.len && hdaddr.type == temp->hwaddr.type) {
770			dprintf(LOG_DEBUG, "%s"" found interface %s iaid %u",
771				FNAME, ifname, temp->iaid);
772			return temp->iaid;
773		} else
774			continue;
775	}
776	return 0;
777}
778
779int
780create_iaid(struct iaid_table *iaidtab, int num_device)
781{
782	struct iaid_table *temp = iaidtab;
783	char buff[1024];
784	struct ifconf ifc;
785	struct ifreq *ifr;
786	int i;
787
788	ifc.ifc_len = sizeof(buff);
789	ifc.ifc_buf = buff;
790	if (ioctl(nlsock, SIOCGIFCONF, &ifc) < 0) {
791		dprintf(LOG_ERR, "%s" "ioctl SIOCGIFCONF", FNAME);
792		return -1;
793	}
794
795	ifr = ifc.ifc_req;
796	for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0 && num_device < MAX_DEVICE;
797	     ifr++) {
798		if (!strcmp(ifr->ifr_name, "lo")) continue;
799		temp->hwaddr.len = gethwid(temp->hwaddr.data, sizeof(temp->hwaddr.data), ifr->ifr_name, &temp->hwaddr.type);
800		switch (temp->hwaddr.type) {
801		case ARPHRD_ETHER:
802		case ARPHRD_IEEE802:
803			memcpy(&temp->iaid, temp->hwaddr.data, sizeof(temp->iaid));
804			break;
805		case ARPHRD_PPP:
806			temp->iaid = do_hash(ifr->ifr_name,sizeof(ifr->ifr_name))
807				+ if_nametoindex(ifr->ifr_name);
808			break;
809		default:
810			dprintf(LOG_INFO, "doesn't support %s address family %d",
811				ifr->ifr_name, temp->hwaddr.type);
812			continue;
813		}
814		dprintf(LOG_DEBUG, "%s"" create iaid %u for interface %s",
815			FNAME, temp->iaid, ifr->ifr_name);
816		num_device++;
817		temp++;
818	}
819	return num_device;
820}
821