1/*	$Id: dhcp6c.c,v 1.1.1.1 2006/12/04 00:45:23 Exp $	*/
2/*	ported from KAME: dhcp6c.c,v 1.97 2002/09/24 14:20:49 itojun Exp */
3
4/*
5 * Copyright (C) 1998 and 1999 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/uio.h>
36#include <sys/stat.h>
37#include <sys/ioctl.h>
38#include <unistd.h>
39#include <errno.h>
40#if TIME_WITH_SYS_TIME
41# include <sys/time.h>
42# include <sys/timeb.h>
43# include <time.h>
44#else
45# if HAVE_SYS_TIME_H
46#  include <sys/time.h>
47# else
48#  include <time.h>
49# endif
50#endif
51#include <net/if.h>
52#include <linux/sockios.h>
53#if defined(__FreeBSD__) && __FreeBSD__ >= 3
54#include <net/if_var.h>
55#endif
56
57#include <arpa/inet.h>
58#include <netdb.h>
59
60#include <signal.h>
61#include <stdio.h>
62#include <stdarg.h>
63#include <syslog.h>
64#include <stdlib.h>
65#include <unistd.h>
66#include <string.h>
67#include <err.h>
68#include <ifaddrs.h>
69
70#include "queue.h"
71#include "dhcp6.h"
72#include "config.h"
73#include "common.h"
74#include "timer.h"
75#include "lease.h"
76
77static int debug = 0;
78static u_long sig_flags = 0;
79#define SIGF_TERM 0x1
80#define SIGF_HUP 0x2
81#define SIGF_CLEAN 0x4
82
83#define DHCP6S_VALID_REPLY(a) \
84	(a == DHCP6S_REQUEST || a == DHCP6S_RENEW || \
85	 a == DHCP6S_REBIND || a == DHCP6S_DECLINE || \
86	 a == DHCP6S_RELEASE || a == DHCP6S_CONFIRM || \
87	 a == DHCP6S_INFOREQ)
88
89
90const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_CLIENT;
91
92static char *device = NULL;
93static int num_device = 0;
94static struct iaid_table iaidtab[100];
95static u_int8_t client6_request_flag = 0;
96static	char leasename[100];
97
98#define CLIENT6_RELEASE_ADDR	0x1
99#define CLIENT6_CONFIRM_ADDR	0x2
100#define CLIENT6_REQUEST_ADDR	0x4
101#define CLIENT6_DECLINE_ADDR	0x8
102
103#define CLIENT6_INFO_REQ	0x10
104
105int insock;	/* inbound udp port */
106//int outsock;	/* outbound udp port */
107int nlsock;
108
109extern char *raproc_file;
110extern char *ifproc_file;
111extern struct ifproc_info *dadlist;
112extern FILE *client6_lease_file;
113extern struct dhcp6_iaidaddr client6_iaidaddr;
114FILE *dhcp6_resolv_file;
115static const struct sockaddr_in6 *sa6_allagent;
116static struct duid client_duid;
117
118static void usage __P((void));
119static void client6_init __P((char *));
120static void client6_ifinit __P((char *));
121void free_servers __P((struct dhcp6_if *));
122static void free_resources __P((struct dhcp6_if *));
123static int create_request_list __P((int));
124static void client6_mainloop __P((void));
125static void process_signals __P((void));
126static struct dhcp6_serverinfo *find_server __P((struct dhcp6_if *,
127						 struct duid *));
128static struct dhcp6_serverinfo *allocate_newserver __P((struct dhcp6_if *, struct dhcp6_optinfo *));
129static struct dhcp6_serverinfo *select_server __P((struct dhcp6_if *));
130void client6_send __P((struct dhcp6_event *));
131int client6_send_newstate __P((struct dhcp6_if *, int));
132static void client6_recv __P((void));
133static int client6_recvadvert __P((struct dhcp6_if *, struct dhcp6 *,
134				   ssize_t, struct dhcp6_optinfo *));
135static int client6_recvreply __P((struct dhcp6_if *, struct dhcp6 *,
136				  ssize_t, struct dhcp6_optinfo *));
137static void client6_signal __P((int));
138static struct dhcp6_event *find_event_withid __P((struct dhcp6_if *,
139						  u_int32_t));
140static struct dhcp6_timer *check_lease_file_timo __P((void *));
141static struct dhcp6_timer *check_link_timo __P((void *));
142static struct dhcp6_timer *check_dad_timo __P((void *));
143static void setup_check_timer __P((struct dhcp6_if *));
144static void setup_interface __P((char *));
145struct dhcp6_timer *client6_timo __P((void *));
146extern int client6_ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_addr *));
147extern struct dhcp6_timer *syncfile_timo __P((void *));
148extern int radvd_parse (struct dhcp6_iaidaddr *, int);
149
150#define DHCP6C_CONF "/tmp/dhcp6c.conf"
151#define DHCP6C_USER_CONF "/tmp/dhcp6c_user.conf"
152#define DHCP6C_PIDFILE "/var/run/dhcpv6/dhcp6c.pid"
153#define DUID_FILE "/tmp/dhcp6c_duid"
154
155static int pid;
156char client6_lease_temp[256];
157struct dhcp6_list request_list;
158struct dhcp6_list request_prefix_list;
159
160/* For testing purpose */
161u_int32_t xid_solicit = 0;
162u_int32_t xid_request = 0;
163
164/* User secondary conf file to store info such as user-class */
165int parse_user_file(char *user_file)
166{
167    FILE *fp = NULL;
168    char line[1024];
169    char user_class_key[] = "user_class";
170    char *newline1;
171    char *newline2;
172
173    /* Initial client user class to empty string */
174    set_dhcpc_user_class("");
175
176    fp = fopen(user_file, "r");
177    if (fp != NULL)
178    {
179        while (!feof(fp))
180        {
181            fgets(line, sizeof(line), fp);
182
183            /* Remove trailing newline characters, if any */
184            newline1 = strstr(line, "\r");
185            newline2 = strstr(line, "\n");
186            if (newline1)
187                *newline1 = '\0';
188            if (newline2)
189                *newline2 = '\0';
190
191            /* Extract the key */
192            if (strncmp(line, user_class_key, strlen(user_class_key)) == 0)
193                set_dhcpc_user_class(&line[strlen(user_class_key)+1]);
194        }
195        fclose(fp);
196    }
197    return 0;
198}
199
200/* Return the interface where dhcp is run on */
201char* get_dhcpc_dev_name(void)
202{
203    return device;
204}
205
206int
207main(argc, argv)
208	int argc;
209	char **argv;
210{
211	int ch;
212	char *progname, *conffile = DHCP6C_CONF;
213    char *user_file = DHCP6C_USER_CONF;
214	FILE *pidfp;
215	char *addr;
216
217	pid = getpid();
218	srandom(time(NULL) & pid);
219
220	if ((progname = strrchr(*argv, '/')) == NULL)
221		progname = *argv;
222	else
223		progname++;
224
225	TAILQ_INIT(&request_list);
226	TAILQ_INIT(&request_prefix_list);
227
228    /* Since 'user class' string may contain any printable char.
229     * The current dhcp6s config file parsing (using yyparse)
230     * can't handle this properly.
231     * So we put user-class string in a separate file,
232     *  specified using '-u' option.
233     */
234	//while ((ch = getopt(argc, argv, "c:r:R:P:dDfI")) != -1) {
235	while ((ch = getopt(argc, argv, "c:u:r:R:P:dDfI")) != -1) {
236		switch (ch) {
237		case 'c':
238			conffile = optarg;
239			break;
240        /* Another config file to store info, such as user-class, etc */
241        case 'u':
242            user_file = optarg;
243            break;
244		case 'P':
245			client6_request_flag |= CLIENT6_REQUEST_ADDR;
246			for (addr = strtok(optarg, " "); addr; addr = strtok(NULL, " ")) {
247				struct dhcp6_listval *lv;
248				if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv)))
249				    == NULL) {
250					dprintf(LOG_ERR, "failed to allocate memory");
251					exit(1);
252				}
253				memset(lv, 0, sizeof(*lv));
254				if (inet_pton(AF_INET6, strtok(addr, "/"),
255				    &lv->val_dhcp6addr.addr) < 1) {
256					dprintf(LOG_ERR,
257						"invalid ipv6address for release");
258					usage();
259					exit(1);
260				}
261				lv->val_dhcp6addr.type = IAPD;
262				lv->val_dhcp6addr.plen = atoi(strtok(NULL, "/"));
263				lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
264				TAILQ_INSERT_TAIL(&request_list, lv, link);
265			}
266			break;
267
268		case 'R':
269			client6_request_flag |= CLIENT6_REQUEST_ADDR;
270			for (addr = strtok(optarg, " "); addr; addr = strtok(NULL, " ")) {
271				struct dhcp6_listval *lv;
272				if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv)))
273				    == NULL) {
274					dprintf(LOG_ERR, "failed to allocate memory");
275					exit(1);
276				}
277				memset(lv, 0, sizeof(*lv));
278				if (inet_pton(AF_INET6, addr, &lv->val_dhcp6addr.addr) < 1) {
279					dprintf(LOG_ERR,
280						"invalid ipv6address for release");
281					usage();
282					exit(1);
283				}
284				lv->val_dhcp6addr.type = IANA;
285				lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
286				TAILQ_INSERT_TAIL(&request_list, lv, link);
287			}
288			break;
289		case 'r':
290			client6_request_flag |= CLIENT6_RELEASE_ADDR;
291			if (strcmp(optarg, "all")) {
292				for (addr = strtok(optarg, " "); addr;
293				     addr = strtok(NULL, " ")) {
294					struct dhcp6_listval *lv;
295					if ((lv = (struct dhcp6_listval *)malloc(sizeof(*lv)))
296					    == NULL) {
297						dprintf(LOG_ERR, "failed to allocate memory");
298						exit(1);
299					}
300					memset(lv, 0, sizeof(*lv));
301					if (inet_pton(AF_INET6, addr,
302					    &lv->val_dhcp6addr.addr) < 1) {
303						dprintf(LOG_ERR,
304							"invalid ipv6address for release");
305						usage();
306						exit(1);
307					}
308					lv->val_dhcp6addr.type = IANA;
309					TAILQ_INSERT_TAIL(&request_list, lv, link);
310				}
311			}
312			break;
313		case 'I':
314			client6_request_flag |= CLIENT6_INFO_REQ;
315			break;
316		case 'd':
317			debug = 1;
318			break;
319		case 'D':
320			debug = 2;
321			break;
322		case 'f':
323			foreground++;
324			break;
325		default:
326			usage();
327			exit(0);
328		}
329	}
330	argc -= optind;
331	argv += optind;
332
333	if (argc != 1) {
334		usage();
335		exit(0);
336	}
337	device = argv[0];
338
339	if (foreground == 0) {
340		if (daemon(0, 0) < 0)
341			err(1, "daemon");
342		openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
343	}
344	setloglevel(debug);
345
346	/* dump current PID */
347	if ((pidfp = fopen(DHCP6C_PIDFILE, "w")) != NULL) {
348		fprintf(pidfp, "%d\n", pid);
349		fclose(pidfp);
350	}
351
352	ifinit(device);
353
354    /* Parse the second conf file, before reading original config file */
355    parse_user_file(user_file);
356
357	if ((cfparse(conffile)) != 0) {
358		dprintf(LOG_ERR, "%s" "failed to parse configuration file",
359			FNAME);
360		exit(1);
361	}
362
363    /* Clear SIP and NTP servers params upon restart */
364    system("nvram set ipv6_sip_servers=\"\"");
365    system("nvram set ipv6_ntp_servers=\"\"");
366
367	client6_init(device);
368	client6_ifinit(device);
369	client6_mainloop();
370	exit(0);
371}
372
373static void
374usage()
375{
376
377	fprintf(stderr,
378	"usage: dhcpc [-c configfile] [-r all or (ipv6address ipv6address...)]\n"
379	"       [-R (ipv6 address ipv6address...) [-dDIf] interface\n");
380}
381
382/*------------------------------------------------------------*/
383
384void
385client6_init(device)
386	char *device;
387{
388	struct addrinfo hints, *res;
389	static struct sockaddr_in6 sa6_allagent_storage;
390	int error, on = 1;
391	struct dhcp6_if *ifp;
392	int ifidx;
393	char linklocal[64];
394	struct in6_addr lladdr;
395
396	ifidx = if_nametoindex(device);
397	if (ifidx == 0) {
398		dprintf(LOG_ERR, "if_nametoindex(%s)", device);
399		exit(1);
400	}
401
402	/* get our DUID */
403	if (get_duid(DUID_FILE, device, &client_duid)) {
404		dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME);
405		exit(1);
406	}
407	if (get_linklocal(device, &lladdr) < 0) {
408		exit(1);
409	}
410	if (inet_ntop(AF_INET6, &lladdr, linklocal, sizeof(linklocal)) < 0) {
411		exit(1);
412	}
413	dprintf(LOG_DEBUG, "link local addr is %s", linklocal);
414
415	memset(&hints, 0, sizeof(hints));
416	hints.ai_family = PF_INET6;
417	hints.ai_socktype = SOCK_DGRAM;
418	hints.ai_protocol = IPPROTO_UDP;
419	hints.ai_flags = 0;
420	error = getaddrinfo(linklocal, DH6PORT_DOWNSTREAM, &hints, &res);
421	if (error) {
422		dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
423			FNAME, strerror(error));
424		exit(1);
425	}
426	insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
427	if (insock < 0) {
428		dprintf(LOG_ERR, "%s" "socket(inbound)", FNAME);
429		exit(1);
430	}
431#ifdef IPV6_RECVPKTINFO
432	if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
433		       sizeof(on)) < 0) {
434		dprintf(LOG_ERR, "%s"
435			"setsockopt(inbound, IPV6_RECVPKTINFO): %s",
436			FNAME, strerror(errno));
437		exit(1);
438	}
439#else
440	if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
441		       sizeof(on)) < 0) {
442		dprintf(LOG_ERR, "%s"
443			"setsockopt(inbound, IPV6_PKTINFO): %s",
444			FNAME, strerror(errno));
445		exit(1);
446	}
447#endif
448	((struct sockaddr_in6 *)(res->ai_addr))->sin6_scope_id = ifidx;
449	dprintf(LOG_DEBUG, "res addr is %s/%d", addr2str(res->ai_addr), res->ai_addrlen);
450	if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) {
451		dprintf(LOG_ERR, "%s" "bind(inbound): %s",
452			FNAME, strerror(errno));
453		exit(1);
454	}
455	freeaddrinfo(res);
456
457	hints.ai_flags = 0;
458	error = getaddrinfo(linklocal, DH6PORT_UPSTREAM, &hints, &res);
459	if (error) {
460		dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
461			FNAME, gai_strerror(error));
462		exit(1);
463	}
464#if 0
465	outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
466	if (outsock < 0) {
467		dprintf(LOG_ERR, "%s" "socket(outbound): %s",
468			FNAME, strerror(errno));
469		exit(1);
470	}
471	if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
472			&ifidx, sizeof(ifidx)) < 0) {
473		dprintf(LOG_ERR, "%s"
474			"setsockopt(outbound, IPV6_MULTICAST_IF): %s",
475			FNAME, strerror(errno));
476		exit(1);
477	}
478	((struct sockaddr_in6 *)(res->ai_addr))->sin6_scope_id = ifidx;
479	if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) {
480		dprintf(LOG_ERR, "%s" "bind(outbound): %s",
481			FNAME, strerror(errno));
482		exit(1);
483	}
484#endif
485	freeaddrinfo(res);
486	/* open a socket to watch the off-on link for confirm messages */
487	if ((nlsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
488		dprintf(LOG_ERR, "%s" "open a socket: %s",
489			FNAME, strerror(errno));
490		exit(1);
491	}
492	memset(&hints, 0, sizeof(hints));
493	hints.ai_family = PF_INET6;
494	hints.ai_socktype = SOCK_DGRAM;
495	hints.ai_protocol = IPPROTO_UDP;
496	error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res);
497	if (error) {
498		dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
499			FNAME, gai_strerror(error));
500		exit(1);
501	}
502	memcpy(&sa6_allagent_storage, res->ai_addr, res->ai_addrlen);
503	sa6_allagent = (const struct sockaddr_in6 *)&sa6_allagent_storage;
504	freeaddrinfo(res);
505
506	/* client interface configuration */
507	if ((ifp = find_ifconfbyname(device)) == NULL) {
508		dprintf(LOG_ERR, "%s" "interface %s not configured",
509			FNAME, device);
510		exit(1);
511	}
512	//ifp->outsock = outsock;
513
514	if (signal(SIGHUP, client6_signal) == SIG_ERR) {
515		dprintf(LOG_WARNING, "%s" "failed to set signal: %s",
516			FNAME, strerror(errno));
517		exit(1);
518	}
519	if (signal(SIGTERM|SIGKILL, client6_signal) == SIG_ERR) {
520		dprintf(LOG_WARNING, "%s" "failed to set signal: %s",
521			FNAME, strerror(errno));
522		exit(1);
523	}
524	if (signal(SIGINT, client6_signal) == SIG_ERR) {
525		dprintf(LOG_WARNING, "%s" "failed to set signal: %s",
526			FNAME, strerror(errno));
527		exit(1);
528	}
529}
530
531static void
532client6_ifinit(char *device)
533{
534	struct dhcp6_if *ifp = dhcp6_if;
535	struct dhcp6_event *ev;
536	char iaidstr[20];
537
538	dhcp6_init_iaidaddr();
539	/* get iaid for each interface */
540	if (num_device == 0) {
541		if ((num_device = create_iaid(&iaidtab[0], num_device)) < 0)
542			exit(1);
543        /* Per Netgear spec, use 1 as the IAID for IA_NA */
544		//ifp->iaidinfo.iaid = get_iaid(ifp->ifname, &iaidtab[0], num_device);
545		ifp->iaidinfo.iaid = 1; //get_iaid(ifp->ifname, &iaidtab[0], num_device);
546		if (ifp->iaidinfo.iaid == 0) {
547			dprintf(LOG_DEBUG, "%s"
548				"interface %s iaid failed to be created",
549				FNAME, ifp->ifname);
550			exit(1);
551		}
552		dprintf(LOG_DEBUG, "%s" "interface %s iaid is %u",
553			FNAME, ifp->ifname, ifp->iaidinfo.iaid);
554	}
555	client6_iaidaddr.ifp = ifp;
556	memcpy(&client6_iaidaddr.client6_info.iaidinfo, &ifp->iaidinfo,
557			sizeof(client6_iaidaddr.client6_info.iaidinfo));
558	duidcpy(&client6_iaidaddr.client6_info.clientid, &client_duid);
559	/* parse the lease file */
560	strcpy(leasename, PATH_CLIENT6_LEASE);
561	sprintf(iaidstr, "%u", ifp->iaidinfo.iaid);
562	strcat(leasename, iaidstr);
563	if ((client6_lease_file =
564		init_leases(leasename)) == NULL) {
565			dprintf(LOG_ERR, "%s" "failed to parse lease file", FNAME);
566		exit(1);
567	}
568	strcpy(client6_lease_temp, leasename);
569	strcat(client6_lease_temp, "XXXXXX");
570	client6_lease_file =
571		sync_leases(client6_lease_file, leasename, client6_lease_temp);
572	if (client6_lease_file == NULL)
573		exit(1);
574	if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) {
575		struct dhcp6_listval *lv;
576		if (!(client6_request_flag & CLIENT6_REQUEST_ADDR) &&
577				!(client6_request_flag & CLIENT6_RELEASE_ADDR))
578			client6_request_flag |= CLIENT6_CONFIRM_ADDR;
579		if (TAILQ_EMPTY(&request_list)) {
580			if (create_request_list(1) < 0)
581				exit(1);
582		} else if (client6_request_flag & CLIENT6_RELEASE_ADDR) {
583			for (lv = TAILQ_FIRST(&request_list); lv;
584					lv = TAILQ_NEXT(lv, link)) {
585				if (dhcp6_find_lease(&client6_iaidaddr,
586						&lv->val_dhcp6addr) == NULL) {
587					dprintf(LOG_INFO, "this address %s is not"
588						" leased by this client",
589					    in6addr2str(&lv->val_dhcp6addr.addr,0));
590					exit(0);
591				}
592			}
593		}
594	} else if (client6_request_flag & CLIENT6_RELEASE_ADDR) {
595		dprintf(LOG_INFO, "no ipv6 addresses are leased by client");
596		exit(0);
597	}
598	setup_interface(ifp->ifname);
599	ifp->link_flag |= IFF_RUNNING;
600
601	/* get addrconf prefix from kernel */
602	(void)get_if_rainfo(ifp);
603
604	/* set up check link timer and sync file timer */
605	if ((ifp->link_timer =
606	    dhcp6_add_timer(check_link_timo, ifp)) < 0) {
607		dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME);
608		exit(1);
609	}
610	if ((ifp->sync_timer = dhcp6_add_timer(check_lease_file_timo, ifp)) < 0) {
611		dprintf(LOG_ERR, "%s" "failed to create a timer", FNAME);
612		exit(1);
613	}
614	/* DAD timer set up after getting the address */
615	ifp->dad_timer = NULL;
616	/* create an event for the initial delay */
617	if ((ev = dhcp6_create_event(ifp, DHCP6S_INIT)) == NULL) {
618		dprintf(LOG_ERR, "%s" "failed to create an event",
619			FNAME);
620		exit(1);
621	}
622	ifp->servers = NULL;
623	ev->ifp->current_server = NULL;
624	TAILQ_INSERT_TAIL(&ifp->event_list, ev, link);
625	if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) {
626		dprintf(LOG_ERR, "%s" "failed to add a timer for %s",
627			FNAME, ifp->ifname);
628		exit(1);
629	}
630	dhcp6_reset_timer(ev);
631}
632
633static void
634free_resources(struct dhcp6_if *ifp)
635{
636	struct dhcp6_event *ev, *ev_next;
637	struct dhcp6_lease *sp, *sp_next;
638	struct stat buf;
639	if (client6_iaidaddr.client6_info.type == IAPD &&
640	    !TAILQ_EMPTY(&client6_iaidaddr.lease_list))
641		radvd_parse(&client6_iaidaddr, ADDR_REMOVE);
642	else {
643		for (sp = TAILQ_FIRST(&client6_iaidaddr.lease_list); sp; sp = sp_next) {
644			sp_next = TAILQ_NEXT(sp, link);
645			if (client6_ifaddrconf(IFADDRCONF_REMOVE, &sp->lease_addr) != 0)
646				dprintf(LOG_INFO, "%s" "deconfiging address %s failed",
647					FNAME, in6addr2str(&sp->lease_addr.addr, 0));
648		}
649	}
650	dprintf(LOG_DEBUG, "%s" " remove all events on interface", FNAME);
651	/* cancel all outstanding events for each interface */
652	for (ev = TAILQ_FIRST(&ifp->event_list); ev; ev = ev_next) {
653		ev_next = TAILQ_NEXT(ev, link);
654		dhcp6_remove_event(ev);
655	}
656	{
657		/* restore /etc/radv.conf.bak back to /etc/radvd.conf */
658		if (!lstat(RADVD_CONF_BAK_FILE, &buf))
659			rename(RADVD_CONF_BAK_FILE, RADVD_CONF_FILE);
660		/* restore /etc/resolv.conf.dhcpv6.bak back to /etc/resolv.conf */
661		if (!lstat(RESOLV_CONF_BAK_FILE, &buf)) {
662			if (rename(RESOLV_CONF_BAK_FILE, RESOLV_CONF_FILE))
663				dprintf(LOG_ERR, "%s" " failed to backup resolv.conf", FNAME);
664		}
665	}
666	free_servers(ifp);
667}
668
669static void
670process_signals()
671{
672	if ((sig_flags & SIGF_TERM)) {
673		dprintf(LOG_INFO, FNAME "exiting");
674
675        /* Release IANA/IAPD before exiting */
676        create_request_list(0);
677        client6_send_newstate(dhcp6_if, DHCP6S_RELEASE);
678
679		free_resources(dhcp6_if);
680		unlink(DHCP6C_PIDFILE);
681		exit(0);
682	}
683	if ((sig_flags & SIGF_HUP)) {
684		dprintf(LOG_INFO, FNAME "restarting");
685
686        /* Release IANA/IAPD before restarting */
687        create_request_list(0);
688        client6_send_newstate(dhcp6_if, DHCP6S_RELEASE);
689
690		free_resources(dhcp6_if);
691		client6_ifinit(device);
692	}
693	if ((sig_flags & SIGF_CLEAN)) {
694
695        /* Release IANA/IAPD before exiting */
696        create_request_list(0);
697        client6_send_newstate(dhcp6_if, DHCP6S_RELEASE);
698
699		free_resources(dhcp6_if);
700		exit(0);
701	}
702	sig_flags = 0;
703}
704
705static void
706client6_mainloop()
707{
708	struct timeval *w;
709	int ret;
710	fd_set r;
711
712	while(1) {
713		if (sig_flags)
714			process_signals();
715		w = dhcp6_check_timer();
716
717		FD_ZERO(&r);
718		FD_SET(insock, &r);
719
720		ret = select(insock + 1, &r, NULL, NULL, w);
721		switch (ret) {
722		case -1:
723			if (errno != EINTR) {
724				dprintf(LOG_ERR, "%s" "select: %s",
725				    FNAME, strerror(errno));
726				exit(1);
727			}
728			break;
729		case 0:	/* timeout */
730			break;	/* dhcp6_check_timer() will treat the case */
731		default: /* received a packet */
732			client6_recv();
733		}
734	}
735}
736
737struct dhcp6_timer *
738client6_timo(arg)
739	void *arg;
740{
741	struct dhcp6_event *ev = (struct dhcp6_event *)arg;
742	struct dhcp6_if *ifp;
743	struct timeval now;
744
745	ifp = ev->ifp;
746	ev->timeouts++;
747	gettimeofday(&now, NULL);
748	if ((ev->max_retrans_cnt && ev->timeouts >= ev->max_retrans_cnt) ||
749	    (ev->max_retrans_dur && (now.tv_sec - ev->start_time.tv_sec)
750	     >= ev->max_retrans_dur)) {
751		dprintf(LOG_INFO, "%s" "no responses were received", FNAME);
752
753        /* WNR3500L TD170, after multiple re-transmit of REQUEST
754         * message, if we did not receive a valid response,
755         * go back to SOLICIT state */
756        if (ev->state == DHCP6S_REQUEST) {
757            ev->timeouts = 0;
758            free_servers(ifp);
759            ev->state = DHCP6S_SOLICIT;
760            dhcp6_set_timeoparam(ev);
761            client6_send(ev);
762            dhcp6_reset_timer(ev);
763            return (ev->timer);
764        }
765
766		dhcp6_remove_event(ev);
767		return (NULL);
768	}
769
770	switch(ev->state) {
771	case DHCP6S_INIT:
772		/* From INIT state client could
773		 * go to CONFIRM state if the client reboots;
774		 * go to RELEASE state if the client issues a release;
775		 * go to INFOREQ state if the client requests info-only;
776		 * go to SOLICIT state if the client requests addresses;
777		 */
778		ev->timeouts = 0; /* indicate to generate a new XID. */
779		/*
780		 * three cases client send information request:
781		 * 1. configuration file includes information-only
782		 * 2. command line includes -I
783		 * 3. check interface flags if managed bit isn't set and
784		 *    if otherconf bit set by RA
785		 *    and information-only, conmand line -I are not set.
786		 */
787		if ((ifp->send_flags & DHCIFF_INFO_ONLY) ||
788
789			/* Ignore the "M" and "O" flags in RA. Just send DHCP solicit */
790			(client6_request_flag & CLIENT6_INFO_REQ) /* ||
791			(!(ifp->ra_flag & IF_RA_MANAGED) &&
792			 (ifp->ra_flag & IF_RA_OTHERCONF))*/ )
793
794			ev->state = DHCP6S_INFOREQ;
795		else if (client6_request_flag & CLIENT6_RELEASE_ADDR)
796			/* do release */
797			ev->state = DHCP6S_RELEASE;
798		else if (client6_request_flag & CLIENT6_CONFIRM_ADDR) {
799			struct dhcp6_listval *lv;
800			/* do confirm for reboot for IANA, IATA*/
801			if (client6_iaidaddr.client6_info.type == IAPD)
802				ev->state = DHCP6S_REBIND;
803			else
804				ev->state = DHCP6S_CONFIRM;
805			for (lv = TAILQ_FIRST(&request_list); lv;
806					lv = TAILQ_NEXT(lv, link)) {
807				lv->val_dhcp6addr.preferlifetime = 0;
808				lv->val_dhcp6addr.validlifetime = 0;
809			}
810		} else
811			ev->state = DHCP6S_SOLICIT;
812		dhcp6_set_timeoparam(ev);
813
814        /* In auto-detect mode, we only send the SOLICIT messages
815         * 3 times.
816         */
817        if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) {
818            ev->max_retrans_cnt = SOL_MAX_RC_AUTODETECT;
819			dprintf(LOG_ERR, "%s" "Set SOLICIT message to %d times",
820                    FNAME, SOL_MAX_RC_AUTODETECT);
821        }
822
823	case DHCP6S_SOLICIT:
824		if (ifp->servers) {
825			ifp->current_server = select_server(ifp);
826			if (ifp->current_server == NULL) {
827				/* this should not happen! */
828				dprintf(LOG_ERR, "%s" "can't find a server",
829					FNAME);
830				exit(1);
831			}
832			/* if get the address assginment break */
833			if (!TAILQ_EMPTY(&client6_iaidaddr.lease_list)) {
834				dhcp6_remove_event(ev);
835				return (NULL);
836			}
837			ev->timeouts = 0;
838			ev->state = DHCP6S_REQUEST;
839			dhcp6_set_timeoparam(ev);
840		}
841        /* In auto-detect mode, we need to send two SOLICIT
842         * messages, one with IANA+IAPD, one with IAPD only.
843         */
844        if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) {
845            client6_send(ev);
846            ifp->send_flags |= DHCIFF_PREFIX_DELEGATION;
847            client6_send(ev);
848            ifp->send_flags &=~ DHCIFF_PREFIX_DELEGATION;
849            break;  /* don't fall through */
850        }
851	//case DHCP6S_INFOREQ:
852	case DHCP6S_REQUEST:
853		client6_send(ev);
854		break;
855
856    /* Use different function to send INFO-REQ */
857    case DHCP6S_INFOREQ:
858        client6_send_info_req(ev);
859        break;
860
861	case DHCP6S_RELEASE:
862	case DHCP6S_DECLINE:
863	case DHCP6S_CONFIRM:
864	case DHCP6S_RENEW:
865	case DHCP6S_REBIND:
866		if (!TAILQ_EMPTY(&request_list))
867			client6_send(ev);
868		else {
869			dprintf(LOG_INFO, "%s"
870		    		"all information to be updated were canceled",
871		    		FNAME);
872			dhcp6_remove_event(ev);
873			return (NULL);
874		}
875		break;
876	default:
877		break;
878	}
879	dhcp6_reset_timer(ev);
880	return (ev->timer);
881}
882
883static struct dhcp6_serverinfo *
884select_server(ifp)
885	struct dhcp6_if *ifp;
886{
887	struct dhcp6_serverinfo *s;
888
889	for (s = ifp->servers; s; s = s->next) {
890		if (s->active) {
891			dprintf(LOG_DEBUG, "%s" "picked a server (ID: %s)",
892				FNAME, duidstr(&s->optinfo.serverID));
893			return (s);
894		}
895	}
896
897	return (NULL);
898}
899
900static void
901client6_signal(sig)
902	int sig;
903{
904
905	dprintf(LOG_INFO, FNAME "received a signal (%d)", sig);
906
907	switch (sig) {
908	case SIGTERM:
909		sig_flags |= SIGF_TERM;
910		break;
911	case SIGHUP:
912		sig_flags |= SIGF_HUP;
913		break;
914	case SIGINT:
915	case SIGKILL:
916		sig_flags |= SIGF_CLEAN;
917		break;
918	default:
919		break;
920	}
921}
922
923void
924client6_send(ev)
925	struct dhcp6_event *ev;
926{
927	struct dhcp6_if *ifp;
928	char buf[BUFSIZ];
929	struct sockaddr_in6 dst;
930	struct dhcp6 *dh6;
931	struct dhcp6_optinfo optinfo;
932	ssize_t optlen, len;
933	struct timeval duration, now;
934
935	ifp = ev->ifp;
936
937	dh6 = (struct dhcp6 *)buf;
938	memset(dh6, 0, sizeof(*dh6));
939
940	switch(ev->state) {
941	case DHCP6S_SOLICIT:
942		dh6->dh6_msgtype = DH6_SOLICIT;
943		break;
944	case DHCP6S_REQUEST:
945		if (ifp->current_server == NULL) {
946			dprintf(LOG_ERR, "%s" "assumption failure", FNAME);
947			exit(1);
948		}
949		dh6->dh6_msgtype = DH6_REQUEST;
950		break;
951	case DHCP6S_RENEW:
952		if (ifp->current_server == NULL) {
953			dprintf(LOG_ERR, "%s" "assumption failure", FNAME);
954			exit(1);
955		}
956		dh6->dh6_msgtype = DH6_RENEW;
957		break;
958	case DHCP6S_DECLINE:
959		if (ifp->current_server == NULL) {
960			dprintf(LOG_ERR, "%s" "assumption failure", FNAME);
961			exit(1);
962		}
963		dh6->dh6_msgtype = DH6_DECLINE;
964		break;
965	case DHCP6S_INFOREQ:
966		dh6->dh6_msgtype = DH6_INFORM_REQ;
967		break;
968	case DHCP6S_REBIND:
969		dh6->dh6_msgtype = DH6_REBIND;
970		break;
971	case DHCP6S_CONFIRM:
972		dh6->dh6_msgtype = DH6_CONFIRM;
973		break;
974	case DHCP6S_RELEASE:
975		dh6->dh6_msgtype = DH6_RELEASE;
976		break;
977	default:
978		dprintf(LOG_ERR, "%s" "unexpected state %d", FNAME, ev->state);
979		exit(1);
980	}
981	/*
982	 * construct options
983	 */
984	dhcp6_init_options(&optinfo);
985	if (ev->timeouts == 0) {
986		gettimeofday(&ev->start_time, NULL);
987		optinfo.elapsed_time = 0;
988		/*
989		 * A client SHOULD generate a random number that cannot easily
990		 * be guessed or predicted to use as the transaction ID for
991		 * each new message it sends.
992		 *
993		 * A client MUST leave the transaction-ID unchanged in
994		 * retransmissions of a message. [dhcpv6-26 15.1]
995		 */
996		ev->xid = random() & DH6_XIDMASK;
997
998        /* For Testing purposes !!! */
999        if (ev->state == DHCP6S_SOLICIT && xid_solicit) {
1000            dprintf(LOG_DEBUG, "%s"
1001                "**TESTING** Use user-defined xid_sol %lu", FNAME, xid_solicit);
1002            ev->xid = xid_solicit & DH6_XIDMASK;
1003        } else if (ev->state == DHCP6S_REQUEST && xid_request) {
1004            dprintf(LOG_DEBUG, "%s"
1005                "**TESTING** Use user-defined xid_req %lu", FNAME, xid_request);
1006            ev->xid = xid_request & DH6_XIDMASK;
1007        }
1008
1009		dprintf(LOG_DEBUG, "%s" "ifp %p event %p a new XID (%x) is generated",
1010			FNAME, ifp, ev, ev->xid);
1011	} else {
1012		unsigned int etime;
1013		gettimeofday(&now, NULL);
1014		timeval_sub(&now, &(ev->start_time), &duration);
1015		optinfo.elapsed_time =
1016		etime = (duration.tv_sec) * 100 + (duration.tv_usec) / 10000;
1017		if (etime > DHCP6_ELAPSEDTIME_MAX)
1018			optinfo.elapsed_time = DHCP6_ELAPSEDTIME_MAX;
1019		else
1020			optinfo.elapsed_time = etime;
1021	}
1022	dh6->dh6_xid &= ~ntohl(DH6_XIDMASK);
1023	dh6->dh6_xid |= htonl(ev->xid);
1024	len = sizeof(*dh6);
1025
1026
1027	/* server ID */
1028	switch(ev->state) {
1029	case DHCP6S_REQUEST:
1030	case DHCP6S_RENEW:
1031	case DHCP6S_DECLINE:
1032		if (&ifp->current_server->optinfo == NULL)
1033			exit(1);
1034		dprintf(LOG_DEBUG, "current server ID %s",
1035			duidstr(&ifp->current_server->optinfo.serverID));
1036		if (duidcpy(&optinfo.serverID,
1037		    &ifp->current_server->optinfo.serverID)) {
1038			dprintf(LOG_ERR, "%s" "failed to copy server ID",
1039			    FNAME);
1040			goto end;
1041		}
1042		break;
1043	case DHCP6S_RELEASE:
1044		if (duidcpy(&optinfo.serverID, &client6_iaidaddr.client6_info.serverid)) {
1045			dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME);
1046			goto end;
1047		}
1048		break;
1049	}
1050	/* client ID */
1051	if (duidcpy(&optinfo.clientID, &client_duid)) {
1052		dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME);
1053		goto end;
1054	}
1055
1056	/* User-class */
1057	strcpy(optinfo.user_class, ifp->user_class);
1058
1059	/* option request options */
1060    /* DHCPv6 readylogo: DHCP confirm message should not
1061     *  have request for DNS/Domain list.
1062     */
1063    if (ev->state != DHCP6S_CONFIRM)
1064	if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) {
1065		dprintf(LOG_ERR, "%s" "failed to copy requested options",
1066		    FNAME);
1067		goto end;
1068	}
1069
1070	switch(ev->state) {
1071	case DHCP6S_SOLICIT:
1072		/* rapid commit */
1073		if (ifp->send_flags & DHCIFF_RAPID_COMMIT)
1074			optinfo.flags |= DHCIFF_RAPID_COMMIT;
1075		if (!(ifp->send_flags & DHCIFF_INFO_ONLY) ||
1076		    (client6_request_flag & CLIENT6_REQUEST_ADDR)) {
1077			memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo,
1078					sizeof(optinfo.iaidinfo));
1079			if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION)
1080				optinfo.type = IAPD;
1081			else if (ifp->send_flags & DHCIFF_TEMP_ADDRS)
1082				optinfo.type = IATA;
1083			else
1084				optinfo.type = IANA;
1085		}
1086		/* support for client preferred ipv6 address */
1087		if (client6_request_flag & CLIENT6_REQUEST_ADDR) {
1088			if (dhcp6_copy_list(&optinfo.addr_list, &request_list))
1089				goto end;
1090		}
1091        /* In auto-detect mode, we need to send two SOLICIT
1092         * messages. 2nd one has IAPD only.
1093         */
1094        if ((ifp->send_flags & DHCIFF_SOLICIT_ONLY) &&
1095            (ifp->send_flags & DHCIFF_PREFIX_DELEGATION)) {
1096            optinfo.type = IAPD;
1097        }
1098		break;
1099	case DHCP6S_REQUEST:
1100		if (!(ifp->send_flags & DHCIFF_INFO_ONLY)) {
1101            /* Should copy 'current_server's addr info */
1102#if 0
1103			memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo,
1104					sizeof(optinfo.iaidinfo));
1105#endif
1106			memcpy(&optinfo.iaidinfo, &(ifp->current_server->optinfo.iaidinfo),
1107                    sizeof(optinfo.iaidinfo));
1108			if (dhcp6_copy_list(&optinfo.addr_list, &(ifp->current_server->optinfo.addr_list)))
1109                goto end;
1110			if (dhcp6_copy_list(&optinfo.prefix_list,
1111						&(ifp->current_server->optinfo.prefix_list)))
1112                goto end;
1113			dprintf(LOG_DEBUG, "%s IAID is %u", FNAME, optinfo.iaidinfo.iaid);
1114			if (ifp->send_flags & DHCIFF_TEMP_ADDRS)
1115				optinfo.type = IATA;
1116			else if (ifp->send_flags & DHCIFF_PREFIX_DELEGATION)
1117				optinfo.type = IAPD;
1118			else
1119				optinfo.type = IANA;
1120		}
1121		break;
1122	case DHCP6S_RENEW:
1123	case DHCP6S_REBIND:
1124	case DHCP6S_RELEASE:
1125	case DHCP6S_CONFIRM:
1126	case DHCP6S_DECLINE:
1127		memcpy(&optinfo.iaidinfo, &client6_iaidaddr.client6_info.iaidinfo,
1128			sizeof(optinfo.iaidinfo));
1129		optinfo.type = client6_iaidaddr.client6_info.type;
1130		if (ev->state == DHCP6S_CONFIRM) {
1131			optinfo.iaidinfo.renewtime = 0;
1132			optinfo.iaidinfo.rebindtime = 0;
1133		}
1134		if (!TAILQ_EMPTY(&request_list)) {
1135			if (dhcp6_copy_list(&optinfo.addr_list, &request_list))
1136				goto end;
1137		} else {
1138			if (ev->state == DHCP6S_RELEASE) {
1139				dprintf(LOG_INFO, "release empty address list");
1140				exit(1);
1141			}
1142		}
1143        /* PUt the IAPD prefix in the DHCP packet */
1144		if (!TAILQ_EMPTY(&request_prefix_list)) {
1145			if (dhcp6_copy_list(&optinfo.prefix_list, &request_prefix_list))
1146				goto end;
1147        }
1148		if (client6_request_flag & CLIENT6_RELEASE_ADDR) {
1149			if (dhcp6_update_iaidaddr(&optinfo, ADDR_REMOVE)) {
1150				dprintf(LOG_INFO, "client release failed");
1151				exit(1);
1152			}
1153			if (client6_iaidaddr.client6_info.type == IAPD)
1154				radvd_parse(&client6_iaidaddr, ADDR_REMOVE);
1155		}
1156		break;
1157	default:
1158		break;
1159	}
1160	/* set options in the message */
1161	if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1),
1162					(struct dhcp6opt *)(buf + sizeof(buf)),
1163					&optinfo)) < 0) {
1164		dprintf(LOG_INFO, "%s" "failed to construct options", FNAME);
1165		goto end;
1166	}
1167	len += optlen;
1168
1169	/*
1170	 * Unless otherwise specified, a client sends DHCP messages to the
1171	 * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address.
1172	 * [dhcpv6-26 Section 13.]
1173	 * Our current implementation always follows the case.
1174	 */
1175	switch(ev->state) {
1176	case DHCP6S_REQUEST:
1177	case DHCP6S_RENEW:
1178	case DHCP6S_DECLINE:
1179	case DHCP6S_RELEASE:
1180		if (ifp->current_server &&
1181		    !IN6_IS_ADDR_UNSPECIFIED(&ifp->current_server->server_addr)) {
1182			struct addrinfo hints, *res;
1183			int error;
1184			memset(&hints, 0, sizeof(hints));
1185			hints.ai_family = PF_INET6;
1186			hints.ai_socktype = SOCK_DGRAM;
1187			hints.ai_protocol = IPPROTO_UDP;
1188			error = getaddrinfo(in6addr2str(&ifp->current_server->server_addr,0),
1189				DH6PORT_UPSTREAM, &hints, &res);
1190			if (error) {
1191				dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
1192					FNAME, gai_strerror(error));
1193				exit(1);
1194			}
1195			memcpy(&dst, res->ai_addr, res->ai_addrlen);
1196			break;
1197		}
1198	default:
1199		dst = *sa6_allagent;
1200		break;
1201	}
1202	dst.sin6_scope_id = ifp->linkid;
1203	dprintf(LOG_DEBUG, "send dst if %s addr is %s scope id is %d",
1204		ifp->ifname, addr2str((struct sockaddr *)&dst), ifp->linkid);
1205    /* why use 'outsock' here? */
1206	//if (sendto(ifp->outsock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst,
1207	if (sendto(insock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst,
1208	    sizeof(dst)) == -1) {
1209		dprintf(LOG_ERR, FNAME "transmit failed: %s", strerror(errno));
1210		goto end;
1211	}
1212
1213	dprintf(LOG_DEBUG, "%s" "send %s to %s", FNAME,
1214		dhcp6msgstr(dh6->dh6_msgtype),
1215		addr2str((struct sockaddr *)&dst));
1216
1217  end:
1218	dhcp6_clear_options(&optinfo);
1219	return;
1220}
1221
1222void
1223client6_send_info_req(ev)
1224	struct dhcp6_event *ev;
1225{
1226	struct dhcp6_if *ifp;
1227	char buf[BUFSIZ];
1228	struct sockaddr_in6 dst;
1229	struct dhcp6 *dh6;
1230	struct dhcp6_optinfo optinfo;
1231	ssize_t optlen, len;
1232	struct timeval duration, now;
1233
1234	ifp = ev->ifp;
1235
1236	dh6 = (struct dhcp6 *)buf;
1237	memset(dh6, 0, sizeof(*dh6));
1238
1239	dh6->dh6_msgtype = DH6_INFORM_REQ;
1240
1241	/*
1242	 * construct options
1243	 */
1244	dhcp6_init_options(&optinfo);
1245	if (ev->timeouts == 0) {
1246		gettimeofday(&ev->start_time, NULL);
1247		optinfo.elapsed_time = 0;
1248		/*
1249		 * A client SHOULD generate a random number that cannot easily
1250		 * be guessed or predicted to use as the transaction ID for
1251		 * each new message it sends.
1252		 *
1253		 * A client MUST leave the transaction-ID unchanged in
1254		 * retransmissions of a message. [dhcpv6-26 15.1]
1255		 */
1256		ev->xid = random() & DH6_XIDMASK;
1257		dprintf(LOG_DEBUG, "%s" "ifp %p event %p a new XID (%x) is generated",
1258			FNAME, ifp, ev, ev->xid);
1259	} else {
1260		unsigned int etime;
1261		gettimeofday(&now, NULL);
1262		timeval_sub(&now, &(ev->start_time), &duration);
1263		optinfo.elapsed_time =
1264		etime = (duration.tv_sec) * 100 + (duration.tv_usec) / 10000;
1265		if (etime > DHCP6_ELAPSEDTIME_MAX)
1266			optinfo.elapsed_time = DHCP6_ELAPSEDTIME_MAX;
1267		else
1268			optinfo.elapsed_time = etime;
1269	}
1270	dh6->dh6_xid &= ~ntohl(DH6_XIDMASK);
1271	dh6->dh6_xid |= htonl(ev->xid);
1272	len = sizeof(*dh6);
1273
1274	/* client ID */
1275	if (duidcpy(&optinfo.clientID, &client_duid)) {
1276		dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME);
1277		goto end;
1278	}
1279
1280	/* User-class */
1281	strcpy(optinfo.user_class, ifp->user_class);
1282
1283	/* option request options */
1284	if (dhcp6_copy_list(&optinfo.reqopt_list, &ifp->reqopt_list)) {
1285		dprintf(LOG_ERR, "%s" "failed to copy requested options",
1286		    FNAME);
1287		goto end;
1288	}
1289
1290	/* set options in the message */
1291	if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1),
1292					(struct dhcp6opt *)(buf + sizeof(buf)),
1293					&optinfo)) < 0) {
1294		dprintf(LOG_INFO, "%s" "failed to construct options", FNAME);
1295		goto end;
1296	}
1297	len += optlen;
1298
1299    /* Special hack to add SIP server and NTP server options */
1300    buf[len-3] += 4;
1301    buf[len++] = 0;
1302    buf[len++] = DH6OPT_SIP_SERVERS;
1303    buf[len++] = 0;
1304    buf[len++] = DH6OPT_NTP_SERVERS;
1305
1306    /*
1307	 * Unless otherwise specified, a client sends DHCP messages to the
1308	 * All_DHCP_Relay_Agents_and_Servers or the DHCP_Anycast address.
1309	 * [dhcpv6-26 Section 13.]
1310	 * Our current implementation always follows the case.
1311	 */
1312	switch(ev->state) {
1313	case DHCP6S_REQUEST:
1314	case DHCP6S_RENEW:
1315	case DHCP6S_DECLINE:
1316	case DHCP6S_RELEASE:
1317		if (ifp->current_server &&
1318		    !IN6_IS_ADDR_UNSPECIFIED(&ifp->current_server->server_addr)) {
1319			struct addrinfo hints, *res;
1320			int error;
1321			memset(&hints, 0, sizeof(hints));
1322			hints.ai_family = PF_INET6;
1323			hints.ai_socktype = SOCK_DGRAM;
1324			hints.ai_protocol = IPPROTO_UDP;
1325			error = getaddrinfo(in6addr2str(&ifp->current_server->server_addr,0),
1326				DH6PORT_UPSTREAM, &hints, &res);
1327			if (error) {
1328				dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
1329					FNAME, gai_strerror(error));
1330				exit(1);
1331			}
1332			memcpy(&dst, res->ai_addr, res->ai_addrlen);
1333			break;
1334		}
1335	default:
1336		dst = *sa6_allagent;
1337		break;
1338	}
1339	dst.sin6_scope_id = ifp->linkid;
1340	dprintf(LOG_DEBUG, "send dst if %s addr is %s scope id is %d",
1341		ifp->ifname, addr2str((struct sockaddr *)&dst), ifp->linkid);
1342    /* why use 'outsock' here? */
1343	//if (sendto(ifp->outsock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst,
1344	if (sendto(insock, buf, len, MSG_DONTROUTE, (struct sockaddr *)&dst,
1345	    sizeof(dst)) == -1) {
1346		dprintf(LOG_ERR, FNAME "transmit failed: %s", strerror(errno));
1347		goto end;
1348	}
1349
1350	dprintf(LOG_DEBUG, "%s" "send %s to %s", FNAME,
1351		dhcp6msgstr(dh6->dh6_msgtype),
1352		addr2str((struct sockaddr *)&dst));
1353
1354  end:
1355	dhcp6_clear_options(&optinfo);
1356	return;
1357}
1358
1359static void
1360client6_recv()
1361{
1362	char rbuf[BUFSIZ], cmsgbuf[BUFSIZ];
1363	struct msghdr mhdr;
1364	struct iovec iov;
1365	struct sockaddr_storage from;
1366	struct dhcp6_if *ifp;
1367	struct dhcp6opt *p, *ep;
1368	struct dhcp6_optinfo optinfo;
1369	ssize_t len;
1370	struct dhcp6 *dh6;
1371	struct cmsghdr *cm;
1372	struct in6_pktinfo *pi = NULL;
1373
1374	memset(&iov, 0, sizeof(iov));
1375	memset(&mhdr, 0, sizeof(mhdr));
1376
1377	iov.iov_base = (caddr_t)rbuf;
1378	iov.iov_len = sizeof(rbuf);
1379	mhdr.msg_name = (caddr_t)&from;
1380	mhdr.msg_namelen = sizeof(from);
1381	mhdr.msg_iov = &iov;
1382	mhdr.msg_iovlen = 1;
1383	mhdr.msg_control = (caddr_t)cmsgbuf;
1384	mhdr.msg_controllen = sizeof(cmsgbuf);
1385	if ((len = recvmsg(insock, &mhdr, 0)) < 0) {
1386		dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno));
1387		return;
1388	}
1389
1390	/* detect receiving interface */
1391	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm;
1392	     cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) {
1393		if (cm->cmsg_level == IPPROTO_IPV6 &&
1394		    cm->cmsg_type == IPV6_PKTINFO &&
1395		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
1396			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
1397		}
1398	}
1399	if (pi == NULL) {
1400		dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME);
1401		return;
1402	}
1403	if ((ifp = find_ifconfbyid(pi->ipi6_ifindex)) == NULL) {
1404		dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME,
1405			(unsigned int)pi->ipi6_ifindex);
1406		return;
1407	}
1408	dprintf(LOG_DEBUG, "receive packet info ifname %s, addr is %s scope id is %d",
1409		ifp->ifname, in6addr2str(&pi->ipi6_addr, 0), pi->ipi6_ifindex);
1410	dh6 = (struct dhcp6 *)rbuf;
1411
1412	dprintf(LOG_DEBUG, "%s" "receive %s from %s scope id %d %s", FNAME,
1413		dhcp6msgstr(dh6->dh6_msgtype),
1414		addr2str((struct sockaddr *)&from),
1415		((struct sockaddr_in6 *)&from)->sin6_scope_id,
1416		ifp->ifname);
1417
1418	/* get options */
1419	dhcp6_init_options(&optinfo);
1420	p = (struct dhcp6opt *)(dh6 + 1);
1421	ep = (struct dhcp6opt *)((char *)dh6 + len);
1422
1423    /* Pass some extra arguments to 'dhcp6_get_options'
1424     * to better determine whether this packet is ok or not.
1425     */
1426    struct dhcp6_event *ev;
1427    ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
1428    if (ev == NULL) {
1429        dprintf(LOG_INFO, "%s" "XID mismatch", FNAME);
1430        return;
1431    }
1432    if (dhcp6_get_options(p, ep, &optinfo, dh6->dh6_msgtype,
1433                ev->state, ifp->send_flags) < 0) {
1434	//if (dhcp6_get_options(p, ep, &optinfo) < 0) {
1435		dprintf(LOG_INFO, "%s" "failed to parse options", FNAME);
1436#ifdef TEST
1437		return;
1438#endif
1439	}
1440
1441	switch(dh6->dh6_msgtype) {
1442	case DH6_ADVERTISE:
1443		(void)client6_recvadvert(ifp, dh6, len, &optinfo);
1444		break;
1445	case DH6_REPLY:
1446		(void)client6_recvreply(ifp, dh6, len, &optinfo);
1447		break;
1448	default:
1449		dprintf(LOG_INFO, "%s" "received an unexpected message (%s) "
1450			"from %s", FNAME, dhcp6msgstr(dh6->dh6_msgtype),
1451			addr2str((struct sockaddr *)&from));
1452		break;
1453	}
1454
1455	dhcp6_clear_options(&optinfo);
1456	return;
1457}
1458
1459static int
1460client6_recvadvert(ifp, dh6, len, optinfo0)
1461	struct dhcp6_if *ifp;
1462	struct dhcp6 *dh6;
1463	ssize_t len;
1464	struct dhcp6_optinfo *optinfo0;
1465{
1466	struct dhcp6_serverinfo *newserver;
1467	struct dhcp6_event *ev;
1468	struct dhcp6_listval *lv;
1469
1470	/* find the corresponding event based on the received xid */
1471	ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
1472	if (ev == NULL) {
1473		dprintf(LOG_INFO, "%s" "XID mismatch", FNAME);
1474		return -1;
1475	}
1476	/* if server policy doesn't allow rapid commit
1477	if (ev->state != DHCP6S_SOLICIT ||
1478	    (ifp->send_flags & DHCIFF_RAPID_COMMIT)) {
1479	*/
1480	if (ev->state != DHCP6S_SOLICIT) {
1481		dprintf(LOG_INFO, "%s" "unexpected advertise", FNAME);
1482		return -1;
1483	}
1484
1485	/* packet validation based on Section 15.3 of dhcpv6-26. */
1486	if (optinfo0->serverID.duid_len == 0) {
1487		dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
1488		return -1;
1489	} else {
1490		dprintf(LOG_DEBUG, "%s" "server ID: %s, pref=%2x", FNAME,
1491			duidstr(&optinfo0->serverID),
1492			optinfo0->pref);
1493	}
1494	if (optinfo0->clientID.duid_len == 0) {
1495		dprintf(LOG_INFO, "%s" "no client ID option", FNAME);
1496		return -1;
1497	}
1498	if (duidcmp(&optinfo0->clientID, &client_duid)) {
1499		dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME);
1500		return -1;
1501	}
1502
1503	/*
1504	 * The client MUST ignore any Advertise message that includes a Status
1505	 * Code option containing any error.
1506	 */
1507	for (lv = TAILQ_FIRST(&optinfo0->stcode_list); lv;
1508	     lv = TAILQ_NEXT(lv, link)) {
1509		dprintf(LOG_INFO, "%s" "status code: %s",
1510		    FNAME, dhcp6_stcodestr(lv->val_num));
1511		if (lv->val_num != DH6OPT_STCODE_SUCCESS) {
1512			return (-1);
1513		}
1514	}
1515
1516	/* ignore the server if it is known */
1517	if (find_server(ifp, &optinfo0->serverID)) {
1518		dprintf(LOG_INFO, "%s" "duplicated server (ID: %s)",
1519			FNAME, duidstr(&optinfo0->serverID));
1520		return -1;
1521	}
1522
1523    /* In Ipv6 auto mode, write result to a file */
1524	if (ifp->send_flags & DHCIFF_SOLICIT_ONLY) {
1525        FILE *fp = NULL;
1526        /* For auto-detect mode, if recv ADVERTISE mesg with
1527         * IAPD-only, write to different file.
1528         */
1529        if (optinfo0->type == IAPD)
1530            fp = fopen("/tmp/wan_dhcp6c_iapd", "w");
1531        else
1532            fp = fopen("/tmp/wan_dhcp6c", "w");
1533        if (fp) {
1534            fprintf(fp, "1");
1535            fclose(fp);
1536        }
1537        return 0;
1538    }
1539
1540	newserver = allocate_newserver(ifp, optinfo0);
1541	if (newserver == NULL)
1542		return (-1);
1543
1544    /* for some reason, 'allocate_newserver' did not copy
1545     * the IAID info. So we do it here...
1546     */
1547    memcpy(&(newserver->optinfo.iaidinfo),
1548           &(optinfo0->iaidinfo),
1549           sizeof(struct dhcp6_iaid_info));
1550
1551	/* if the server has an extremely high preference, just use it. */
1552	if (newserver->pref == DH6OPT_PREF_MAX) {
1553		ev->timeouts = 0;
1554		ev->state = DHCP6S_REQUEST;
1555		ifp->current_server = newserver;
1556		dhcp6_set_timeoparam(ev);
1557		dhcp6_reset_timer(ev);
1558		client6_send(ev);
1559
1560	} else if (ifp->servers->next == NULL) {
1561		struct timeval *rest, elapsed, tv_rt, tv_irt, timo;
1562
1563		rest = dhcp6_timer_rest(ev->timer);
1564		tv_rt.tv_sec = (ev->retrans * 1000) / 1000000;
1565		tv_rt.tv_usec = (ev->retrans * 1000) % 1000000;
1566		tv_irt.tv_sec = (ev->init_retrans * 1000) / 1000000;
1567		tv_irt.tv_usec = (ev->init_retrans * 1000) % 1000000;
1568		timeval_sub(&tv_rt, rest, &elapsed);
1569		if (TIMEVAL_LEQ(elapsed, tv_irt))
1570			timeval_sub(&tv_irt, &elapsed, &timo);
1571		else
1572			timo.tv_sec = timo.tv_usec = 0;
1573
1574		dprintf(LOG_DEBUG, "%s" "reset timer for %s to %d.%06d",
1575			FNAME, ifp->ifname,
1576			(int)timo.tv_sec, (int)timo.tv_usec);
1577
1578		dhcp6_set_timer(&timo, ev->timer);
1579	}
1580	/* if the client send preferred addresses reqeust in SOLICIT */
1581	if (!TAILQ_EMPTY(&optinfo0->addr_list))
1582		dhcp6_copy_list(&request_list, &optinfo0->addr_list);
1583	/* Store IAPD to the request_prefix_list, for later use by DHCP renew */
1584	if (!TAILQ_EMPTY(&optinfo0->prefix_list))
1585		dhcp6_copy_list(&request_prefix_list, &optinfo0->prefix_list);
1586	return 0;
1587}
1588
1589static struct dhcp6_serverinfo *
1590find_server(ifp, duid)
1591	struct dhcp6_if *ifp;
1592	struct duid *duid;
1593{
1594	struct dhcp6_serverinfo *s;
1595
1596	for (s = ifp->servers; s; s = s->next) {
1597		if (duidcmp(&s->optinfo.serverID, duid) == 0)
1598			return (s);
1599	}
1600
1601	return (NULL);
1602}
1603
1604static struct dhcp6_serverinfo *
1605allocate_newserver(ifp, optinfo)
1606	struct dhcp6_if *ifp;
1607	struct dhcp6_optinfo *optinfo;
1608{
1609	struct dhcp6_serverinfo *newserver, **sp;
1610
1611	/* keep the server */
1612	if ((newserver = malloc(sizeof(*newserver))) == NULL) {
1613		dprintf(LOG_ERR, "%s" "memory allocation failed for server",
1614			FNAME);
1615		return (NULL);
1616	}
1617	memset(newserver, 0, sizeof(*newserver));
1618	dhcp6_init_options(&newserver->optinfo);
1619	if (dhcp6_copy_options(&newserver->optinfo, optinfo)) {
1620		dprintf(LOG_ERR, "%s" "failed to copy options", FNAME);
1621		free(newserver);
1622		return (NULL);
1623	}
1624	dprintf(LOG_DEBUG, "%s" "new server DUID %s, len %d ",
1625		FNAME, duidstr(&newserver->optinfo.serverID),
1626		newserver->optinfo.serverID.duid_len);
1627	if (optinfo->pref != DH6OPT_PREF_UNDEF)
1628		newserver->pref = optinfo->pref;
1629	if (optinfo->flags & DHCIFF_UNICAST)
1630		memcpy(&newserver->server_addr, &optinfo->server_addr,
1631		       sizeof(newserver->server_addr));
1632	newserver->active = 1;
1633	for (sp = &ifp->servers; *sp; sp = &(*sp)->next) {
1634		if ((*sp)->pref != DH6OPT_PREF_MAX &&
1635		    (*sp)->pref < newserver->pref) {
1636			break;
1637		}
1638	}
1639	newserver->next = *sp;
1640	*sp = newserver;
1641	return newserver;
1642}
1643
1644void
1645free_servers(ifp)
1646	struct dhcp6_if *ifp;
1647{
1648	struct dhcp6_serverinfo *sp, *sp_next;
1649	/* free all servers we've seen so far */
1650	for (sp = ifp->servers; sp; sp = sp_next) {
1651		sp_next = sp->next;
1652		dprintf(LOG_DEBUG, "%s" "removing server (ID: %s)",
1653		    FNAME, duidstr(&sp->optinfo.serverID));
1654		dhcp6_clear_options(&sp->optinfo);
1655		free(sp);
1656	}
1657	ifp->servers = NULL;
1658	ifp->current_server = NULL;
1659}
1660
1661static int not_on_link_count = 0;
1662
1663static int
1664client6_recvreply(ifp, dh6, len, optinfo)
1665	struct dhcp6_if *ifp;
1666	struct dhcp6 *dh6;
1667	ssize_t len;
1668	struct dhcp6_optinfo *optinfo;
1669{
1670	struct dhcp6_listval *lv;
1671	struct dhcp6_event *ev;
1672	int addr_status_code = DH6OPT_STCODE_UNSPECFAIL;
1673	struct dhcp6_serverinfo *newserver;
1674	int newstate = 0;
1675	/* find the corresponding event based on the received xid */
1676	dprintf(LOG_DEBUG, "%s" "reply message XID is (%x)",
1677		FNAME, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
1678	ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
1679	if (ev == NULL) {
1680		dprintf(LOG_INFO, "%s" "XID mismatch", FNAME);
1681		return -1;
1682	}
1683
1684	if (!(DHCP6S_VALID_REPLY(ev->state)) &&
1685	    (ev->state != DHCP6S_SOLICIT ||
1686	     !(optinfo->flags & DHCIFF_RAPID_COMMIT))) {
1687		dprintf(LOG_INFO, "%s" "unexpected reply", FNAME);
1688		return -1;
1689	}
1690
1691	dhcp6_clear_list(&request_list);
1692
1693	/* A Reply message must contain a Server ID option */
1694	if (optinfo->serverID.duid_len == 0) {
1695		dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
1696		return -1;
1697	}
1698	dprintf(LOG_DEBUG, "%s" "serverID is %s len is %d", FNAME,
1699		duidstr(&optinfo->serverID), optinfo->serverID.duid_len);
1700	/* get current server */
1701	switch (ev->state) {
1702	case DHCP6S_SOLICIT:
1703	case DHCP6S_CONFIRM:
1704	case DHCP6S_REBIND:
1705		newserver = allocate_newserver(ifp, optinfo);
1706		if (newserver == NULL)
1707			return (-1);
1708		ifp->current_server = newserver;
1709		duidcpy(&client6_iaidaddr.client6_info.serverid,
1710			&ifp->current_server->optinfo.serverID);
1711		break;
1712	default:
1713		break;
1714	}
1715	/*
1716	 * DUID in the Client ID option (which must be contained for our
1717	 * client implementation) must match ours.
1718	 */
1719	if (optinfo->clientID.duid_len == 0) {
1720		dprintf(LOG_INFO, "%s" "no client ID option", FNAME);
1721		return -1;
1722	}
1723	if (duidcmp(&optinfo->clientID, &client_duid)) {
1724		dprintf(LOG_INFO, "%s" "client DUID mismatch", FNAME);
1725		return -1;
1726	}
1727
1728	if (!TAILQ_EMPTY(&optinfo->dns_list.addrlist) ||
1729	    optinfo->dns_list.domainlist != NULL) {
1730		resolv_parse(&optinfo->dns_list);
1731	}
1732	/*
1733	 * The client MAY choose to report any status code or message from the
1734	 * status code option in the Reply message.
1735	 * [dhcpv6-26 Section 18.1.8]
1736	 */
1737	addr_status_code = 0;
1738	for (lv = TAILQ_FIRST(&optinfo->stcode_list); lv;
1739	     lv = TAILQ_NEXT(lv, link)) {
1740		dprintf(LOG_INFO, "%s" "status code: %s",
1741		    FNAME, dhcp6_stcodestr(lv->val_num));
1742		switch (lv->val_num) {
1743		case DH6OPT_STCODE_SUCCESS:
1744		case DH6OPT_STCODE_UNSPECFAIL:
1745		case DH6OPT_STCODE_NOADDRAVAIL:
1746		case DH6OPT_STCODE_NOPREFIXAVAIL:
1747		case DH6OPT_STCODE_NOBINDING:
1748		case DH6OPT_STCODE_NOTONLINK:
1749		case DH6OPT_STCODE_USEMULTICAST:
1750            if (addr_status_code == 0)
1751    			addr_status_code = lv->val_num;
1752		default:
1753			break;
1754		}
1755	}
1756	switch (addr_status_code) {
1757	case DH6OPT_STCODE_UNSPECFAIL:
1758	case DH6OPT_STCODE_USEMULTICAST:
1759		dprintf(LOG_INFO, "%s" "status code: %s", FNAME,
1760			dhcp6_stcodestr(addr_status_code));
1761		/* retransmit the message with multicast address */
1762		/* how many time allow the retransmission with error status code? */
1763		return -1;
1764	}
1765
1766	/*
1767	 * Update configuration information to be renewed or rebound
1768	 * declined, confirmed, released.
1769	 * Note that the returned list may be empty, in which case
1770	 * the waiting information should be removed.
1771	 */
1772	switch (ev->state) {
1773	case DHCP6S_SOLICIT:
1774		if (optinfo->iaidinfo.iaid == 0)
1775			break;
1776		else if (!optinfo->flags & DHCIFF_RAPID_COMMIT) {
1777			newstate = DHCP6S_REQUEST;
1778			break;
1779		}
1780	case DHCP6S_REQUEST:
1781		/* NotOnLink: 1. SOLICIT
1782		 * NoAddrAvail: Information Request */
1783		switch(addr_status_code) {
1784		case DH6OPT_STCODE_NOTONLINK:
1785			dprintf(LOG_DEBUG, "%s"
1786			    "got a NotOnLink reply for request/rapid commit,"
1787			    " sending solicit.", FNAME);
1788            /* WNR3500L TD170, need to send request without any IP for
1789             *  3 times, then back to solicit state.
1790             */
1791			//newstate = DHCP6S_SOLICIT;
1792            not_on_link_count++;
1793            if (not_on_link_count <= REQ_MAX_RC_NOTONLINK) {
1794                /* Clear the IA / PD address, so they won't appear in the
1795                 * request pkt. */
1796                dhcp6_clear_list(&(ifp->current_server->optinfo.addr_list));
1797                dhcp6_clear_list(&(ifp->current_server->optinfo.prefix_list));
1798                newstate = DHCP6S_REQUEST;
1799            } else {
1800                /* Three times, back to SOLICIT state */
1801                not_on_link_count = 0;
1802                free_servers(ifp);
1803                newstate = DHCP6S_SOLICIT;
1804            }
1805			break;
1806		case DH6OPT_STCODE_NOADDRAVAIL:
1807		case DH6OPT_STCODE_NOPREFIXAVAIL:
1808			dprintf(LOG_DEBUG, "%s"
1809			    "got a NoAddrAvail reply for request/rapid commit,"
1810			    " sending inforeq.", FNAME);
1811            not_on_link_count = 0;
1812			optinfo->iaidinfo.iaid = 0;
1813			newstate = DHCP6S_INFOREQ;
1814			break;
1815		case DH6OPT_STCODE_SUCCESS:
1816		case DH6OPT_STCODE_UNDEFINE:
1817		default:
1818            not_on_link_count = 0;
1819			if (!TAILQ_EMPTY(&optinfo->addr_list)) {
1820				(void)get_if_rainfo(ifp);
1821				dhcp6_add_iaidaddr(optinfo);
1822				if (optinfo->type == IAPD) {
1823					radvd_parse(&client6_iaidaddr, ADDR_UPDATE);
1824                    /* 1. Execute callback now as IAPD only does not need DAD.
1825                     * 2. Send Info-req to get additional info
1826                     */
1827                    dhcp6c_dad_callback();
1828                    newstate = DHCP6S_INFOREQ;
1829                }
1830				else if (ifp->dad_timer == NULL && (ifp->dad_timer =
1831					  dhcp6_add_timer(check_dad_timo, ifp)) < 0) {
1832					dprintf(LOG_INFO, "%s" "failed to create a timer for "
1833						" DAD", FNAME);
1834				}
1835				setup_check_timer(ifp);
1836                /* WNR3500L TD#175, send info-req after we complete the
1837                 *  DAD check. */
1838                //client6_send_info_req(ev);
1839			}
1840			break;
1841		}
1842		break;
1843	case DHCP6S_RENEW:
1844	case DHCP6S_REBIND:
1845		if (client6_request_flag & CLIENT6_CONFIRM_ADDR)
1846			goto rebind_confirm;
1847		/* NoBinding for RENEW, REBIND, send REQUEST */
1848		switch(addr_status_code) {
1849		case DH6OPT_STCODE_NOBINDING:
1850			newstate = DHCP6S_REQUEST;
1851			dprintf(LOG_DEBUG, "%s"
1852			    	  "got a NoBinding reply, sending request.", FNAME);
1853			dhcp6_remove_iaidaddr(&client6_iaidaddr);
1854			break;
1855		case DH6OPT_STCODE_NOADDRAVAIL:
1856		case DH6OPT_STCODE_NOPREFIXAVAIL:
1857		case DH6OPT_STCODE_UNSPECFAIL:
1858			break;
1859		case DH6OPT_STCODE_SUCCESS:
1860		case DH6OPT_STCODE_UNDEFINE:
1861		default:
1862			dhcp6_update_iaidaddr(optinfo, ADDR_UPDATE);
1863			if (optinfo->type == IAPD)
1864				radvd_parse(&client6_iaidaddr, ADDR_UPDATE);
1865			system("killall -SIGUSR1 radvd");
1866            /* Send info-req to get SIP server and NTP server */
1867            client6_send_info_req(ev);
1868			break;
1869		}
1870		break;
1871	case DHCP6S_CONFIRM:
1872		/* NOtOnLink for a Confirm, send SOLICIT message */
1873rebind_confirm:	client6_request_flag &= ~CLIENT6_CONFIRM_ADDR;
1874	switch(addr_status_code) {
1875		struct timeb now;
1876		struct timeval timo;
1877		time_t offset;
1878		case DH6OPT_STCODE_NOTONLINK:
1879		case DH6OPT_STCODE_NOBINDING:
1880		case DH6OPT_STCODE_NOADDRAVAIL:
1881			dprintf(LOG_DEBUG, "%s"
1882				"got a NotOnLink reply for confirm, sending solicit.", FNAME);
1883			/* remove event data list */
1884			free_servers(ifp);
1885			newstate = DHCP6S_SOLICIT;
1886			break;
1887		case DH6OPT_STCODE_SUCCESS:
1888		case DH6OPT_STCODE_UNDEFINE:
1889			dprintf(LOG_DEBUG, "%s" "got an expected reply for confirm", FNAME);
1890			ftime(&now);
1891			client6_iaidaddr.state = ACTIVE;
1892			if ((client6_iaidaddr.timer = dhcp6_add_timer(dhcp6_iaidaddr_timo,
1893						&client6_iaidaddr)) == NULL) {
1894		 		dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
1895					FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid);
1896		 		return (-1);
1897			}
1898			if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) {
1899				client6_iaidaddr.client6_info.iaidinfo.renewtime
1900					= get_min_preferlifetime(&client6_iaidaddr)/2;
1901			}
1902			if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0) {
1903				client6_iaidaddr.client6_info.iaidinfo.rebindtime
1904					= (get_min_preferlifetime(&client6_iaidaddr)*4)/5;
1905			}
1906			offset = now.time - client6_iaidaddr.start_date;
1907			if ( offset > client6_iaidaddr.client6_info.iaidinfo.renewtime)
1908				timo.tv_sec = 0;
1909			else
1910				timo.tv_sec = client6_iaidaddr.client6_info.iaidinfo.renewtime 						- offset;
1911			timo.tv_usec = 0;
1912			dhcp6_set_timer(&timo, client6_iaidaddr.timer);
1913			/* check DAD */
1914			if (optinfo->type != IAPD && ifp->dad_timer == NULL &&
1915			    (ifp->dad_timer = dhcp6_add_timer(check_dad_timo, ifp)) < 0) {
1916				dprintf(LOG_INFO, "%s" "failed to create a timer for "
1917					" DAD", FNAME);
1918			}
1919			setup_check_timer(ifp);
1920			break;
1921		default:
1922			break;
1923		}
1924		break;
1925	case DHCP6S_DECLINE:
1926		/* send REQUEST message to server with none decline address */
1927		dprintf(LOG_DEBUG, "%s"
1928		    "got an expected reply for decline, sending request.", FNAME);
1929        /* Should restart the 4-packet process, from SOLICIT */
1930#if 0
1931		create_request_list(0);
1932		/* remove event data list */
1933		newstate = DHCP6S_REQUEST;
1934#endif
1935        free_servers(ifp);
1936        newstate = DHCP6S_SOLICIT;
1937		break;
1938	case DHCP6S_RELEASE:
1939		dprintf(LOG_INFO, "%s" "got an expected release, exit.", FNAME);
1940		dhcp6_remove_event(ev);
1941		exit(0);
1942	default:
1943		break;
1944	}
1945	dhcp6_remove_event(ev);
1946	if (newstate) {
1947		client6_send_newstate(ifp, newstate);
1948	} else
1949		dprintf(LOG_DEBUG, "%s" "got an expected reply, sleeping.", FNAME);
1950	TAILQ_INIT(&request_list);
1951	return 0;
1952}
1953
1954int
1955client6_send_newstate(ifp, state)
1956	struct dhcp6_if *ifp;
1957	int state;
1958{
1959	struct dhcp6_event *ev;
1960	if ((ev = dhcp6_create_event(ifp, state)) == NULL) {
1961		dprintf(LOG_ERR, "%s" "failed to create an event",
1962			FNAME);
1963		return (-1);
1964	}
1965	if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) {
1966		dprintf(LOG_ERR, "%s" "failed to add a timer for %s",
1967			FNAME, ifp->ifname);
1968		free(ev);
1969		return(-1);
1970	}
1971	TAILQ_INSERT_TAIL(&ifp->event_list, ev, link);
1972	ev->timeouts = 0;
1973	dhcp6_set_timeoparam(ev);
1974    /* WNR3500L TD170, modify the maximum re-send counter of
1975     *  Request message to 3 if a "NotOnLink" status is
1976     *  received.
1977     */
1978    if (state == DHCP6S_REQUEST && not_on_link_count)
1979        ev->max_retrans_cnt = REQ_MAX_RC_NOTONLINK;
1980	dhcp6_reset_timer(ev);
1981
1982    /* Use diff function to send INFO-REQ */
1983    if (state == DHCP6S_INFOREQ)
1984        client6_send_info_req(ev);
1985    else
1986        client6_send(ev);
1987	return 0;
1988}
1989
1990static struct dhcp6_event *
1991find_event_withid(ifp, xid)
1992	struct dhcp6_if *ifp;
1993	u_int32_t xid;
1994{
1995	struct dhcp6_event *ev;
1996
1997	for (ev = TAILQ_FIRST(&ifp->event_list); ev;
1998	     ev = TAILQ_NEXT(ev, link)) {
1999		dprintf(LOG_DEBUG, "%s" "ifp %p event %p id is %x",
2000			FNAME, ifp, ev, ev->xid);
2001		if (ev->xid == xid)
2002			return (ev);
2003	}
2004
2005	return (NULL);
2006}
2007
2008static int
2009create_request_list(int reboot)
2010{
2011	struct dhcp6_lease *cl;
2012	struct dhcp6_listval *lv;
2013	/* create an address list for release all/confirm */
2014	for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl;
2015		cl = TAILQ_NEXT(cl, link)) {
2016		/* IANA, IAPD */
2017		if ((lv = malloc(sizeof(*lv))) == NULL) {
2018			dprintf(LOG_ERR, "%s"
2019				"failed to allocate memory for an ipv6 addr", FNAME);
2020			 exit(1);
2021		}
2022		memcpy(&lv->val_dhcp6addr, &cl->lease_addr,
2023			sizeof(lv->val_dhcp6addr));
2024		lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
2025		TAILQ_INSERT_TAIL(&request_list, lv, link);
2026		/* config the interface for reboot */
2027		if (reboot && client6_iaidaddr.client6_info.type != IAPD &&
2028		    (client6_request_flag & CLIENT6_CONFIRM_ADDR)) {
2029			if (client6_ifaddrconf(IFADDRCONF_ADD, &cl->lease_addr) != 0) {
2030				dprintf(LOG_INFO, "config address failed: %s",
2031					in6addr2str(&cl->lease_addr.addr, 0));
2032				return (-1);
2033			}
2034		}
2035	}
2036	/* update radvd.conf for prefix delegation */
2037	if (reboot && client6_iaidaddr.client6_info.type == IAPD &&
2038	    (client6_request_flag & CLIENT6_CONFIRM_ADDR))
2039		radvd_parse(&client6_iaidaddr, ADDR_UPDATE);
2040	return (0);
2041}
2042
2043static void setup_check_timer(struct dhcp6_if *ifp)
2044{
2045	double d;
2046	struct timeval timo;
2047	d = DHCP6_CHECKLINK_TIME;
2048	timo.tv_sec = (long)d;
2049	timo.tv_usec = 0;
2050	dprintf(LOG_DEBUG, "set timer for checking link ...");
2051	dhcp6_set_timer(&timo, ifp->link_timer);
2052	if (ifp->dad_timer != NULL) {
2053		d = DHCP6_CHECKDAD_TIME;
2054		timo.tv_sec = (long)d;
2055		timo.tv_usec = 0;
2056		dprintf(LOG_DEBUG, "set timer for checking DAD ...");
2057		dhcp6_set_timer(&timo, ifp->dad_timer);
2058	}
2059	d = DHCP6_SYNCFILE_TIME;
2060	timo.tv_sec = (long)d;
2061	timo.tv_usec = 0;
2062	dprintf(LOG_DEBUG, "set timer for syncing file ...");
2063	dhcp6_set_timer(&timo, ifp->sync_timer);
2064	return;
2065}
2066
2067static struct dhcp6_timer
2068*check_lease_file_timo(void *arg)
2069{
2070	struct dhcp6_if *ifp = (struct dhcp6_if *)arg;
2071	double d;
2072	struct timeval timo;
2073	struct stat buf;
2074	FILE *file;
2075	stat(leasename, &buf);
2076	strcpy(client6_lease_temp, leasename);
2077	strcat(client6_lease_temp, "XXXXXX");
2078	if (buf.st_size > MAX_FILE_SIZE) {
2079		file = sync_leases(client6_lease_file, leasename, client6_lease_temp);
2080		if ( file != NULL)
2081			client6_lease_file = file;
2082	}
2083	d = DHCP6_SYNCFILE_TIME;
2084	timo.tv_sec = (long)d;
2085	timo.tv_usec = 0;
2086	dhcp6_set_timer(&timo, ifp->sync_timer);
2087	return ifp->sync_timer;
2088}
2089
2090static struct dhcp6_timer
2091*check_dad_timo(void *arg)
2092{
2093	struct dhcp6_if *ifp = (struct dhcp6_if *)arg;
2094	int newstate = DHCP6S_REQUEST;
2095	if (client6_iaidaddr.client6_info.type == IAPD)
2096		goto end;
2097	dprintf(LOG_DEBUG, "enter checking dad ...");
2098	if (dad_parse(ifproc_file) < 0) {
2099		dprintf(LOG_ERR, "parse /proc/net/if_inet6 failed");
2100		goto end;
2101	}
2102	if (TAILQ_EMPTY(&request_list))
2103		goto end;
2104	/* remove RENEW timer for client6_iaidaddr */
2105	if (client6_iaidaddr.timer != NULL)
2106		dhcp6_remove_timer(client6_iaidaddr.timer);
2107	newstate = DHCP6S_DECLINE;
2108	client6_send_newstate(ifp, newstate);
2109end:
2110	/* one time check for DAD */
2111	dhcp6_remove_timer(ifp->dad_timer);
2112	ifp->dad_timer = NULL;
2113
2114    /* Send info-req to get DNS/SIP/NTP, etc, per Netgear spec. */
2115    if (newstate != DHCP6S_DECLINE) {
2116        dhcp6c_dad_callback();
2117        dprintf(LOG_DEBUG, "send info-req");
2118        newstate = DHCP6S_INFOREQ;
2119        client6_send_newstate(ifp, newstate);
2120    }
2121
2122	return NULL;
2123}
2124
2125static struct dhcp6_timer
2126*check_link_timo(void *arg)
2127{
2128	struct dhcp6_if *ifp = (struct dhcp6_if *)arg;
2129	struct ifreq ifr;
2130	struct timeval timo;
2131	double d;
2132	int newstate;
2133	dprintf(LOG_DEBUG, "enter checking link ...");
2134	strncpy(ifr.ifr_name, dhcp6_if->ifname, IFNAMSIZ);
2135	if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) {
2136		dprintf(LOG_DEBUG, "ioctl SIOCGIFFLAGS failed");
2137		goto settimer;
2138	}
2139	if (ifr.ifr_flags & IFF_RUNNING) {
2140		/* check previous flag
2141		 * set current flag UP */
2142		if (ifp->link_flag & IFF_RUNNING) {
2143			goto settimer;
2144		}
2145		/* check current state ACTIVE */
2146		if (client6_iaidaddr.state == ACTIVE) {
2147			/* remove timer for renew/rebind
2148			 * send confirm for ipv6address or
2149			 * rebind for prefix delegation */
2150			dhcp6_remove_timer(client6_iaidaddr.timer);
2151			client6_request_flag &= CLIENT6_CONFIRM_ADDR;
2152			create_request_list(0);
2153			if (client6_iaidaddr.client6_info.type == IAPD)
2154				newstate = DHCP6S_REBIND;
2155			else
2156				newstate = DHCP6S_CONFIRM;
2157			client6_send_newstate(ifp, newstate);
2158		}
2159		dprintf(LOG_INFO, "interface is from down to up");
2160		ifp->link_flag |= IFF_RUNNING;
2161	} else {
2162		dprintf(LOG_INFO, "interface is down");
2163		/* set flag_prev flag DOWN */
2164		ifp->link_flag &= ~IFF_RUNNING;
2165	}
2166settimer:
2167	d = DHCP6_CHECKLINK_TIME;
2168	timo.tv_sec = (long)d;
2169	timo.tv_usec = 0;
2170	dhcp6_set_timer(&timo, ifp->link_timer);
2171	return ifp->link_timer;
2172}
2173
2174static void
2175setup_interface(char *ifname)
2176{
2177	struct ifreq ifr;
2178	/* check the interface */
2179	strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
2180again:
2181	if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) {
2182		dprintf(LOG_ERR, "ioctl SIOCGIFFLAGS failed");
2183		exit(1);
2184	}
2185	if (!ifr.ifr_flags & IFF_UP) {
2186		ifr.ifr_flags |= IFF_UP;
2187		if (ioctl(nlsock, SIOCSIFFLAGS, &ifr) < 0) {
2188			dprintf(LOG_ERR, "ioctl SIOCSIFFLAGS failed");
2189			exit(1);
2190		}
2191		if (ioctl(nlsock, SIOCGIFFLAGS, &ifr) < 0) {
2192			dprintf(LOG_ERR, "ioctl SIOCGIFFLAGS failed");
2193			exit(1);
2194		}
2195	}
2196	if (!ifr.ifr_flags & IFF_RUNNING) {
2197		dprintf(LOG_INFO, "NIC is not connected to the network, "
2198			"please connect it. dhcp6c is sleeping ...");
2199		sleep(10);
2200		goto again;
2201	}
2202	return;
2203}
2204