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