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