1/*	$Id: common.c,v 1.1.1.1 2006-12-04 00:45:20 Exp $	*/
2/*	ported from KAME: common.c,v 1.65 2002/12/06 01:41:29 suz 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 <linux/sockios.h>
36#include <sys/ioctl.h>
37
38#if TIME_WITH_SYS_TIME
39# include <sys/time.h>
40# include <time.h>
41#else
42# if HAVE_SYS_TIME_H
43#  include <sys/time.h>
44# else
45#  include <time.h>
46# endif
47#endif
48#include <net/if.h>
49#if defined(__FreeBSD__) && __FreeBSD__ >= 3
50#include <net/if_var.h>
51#endif
52#include <net/if_arp.h>
53
54#include <netinet/in.h>
55#include <errno.h>
56#include <limits.h>
57#include <stdio.h>
58#include <stdarg.h>
59#include <syslog.h>
60#include <stdlib.h>
61#include <unistd.h>
62#include <string.h>
63#include <err.h>
64#include <netdb.h>
65#include <ifaddrs.h>
66#include <resolv.h>
67
68#ifdef HAVE_GETIFADDRS
69# ifdef HAVE_IFADDRS_H
70#  define USE_GETIFADDRS
71#  include <ifaddrs.h>
72# endif
73#endif
74
75#include "queue.h"
76#include "dhcp6.h"
77#include "config.h"
78#include "common.h"
79#include "timer.h"
80#include "lease.h"
81
82int foreground;
83int debug_thresh;
84struct dhcp6_if *dhcp6_if;
85struct dns_list dnslist;
86/* Foxconn added start pling 01/25/2010 */
87struct dhcp6_list siplist;
88struct dhcp6_list ntplist;
89/* Foxconn added end pling 01/25/2010 */
90static struct host_conf *host_conflist;
91static int in6_matchflags __P((struct sockaddr *, char *, int));
92ssize_t gethwid __P((char *, int, const char *, u_int16_t *));
93static int get_assigned_ipv6addrs __P((char *, char *,
94					struct dhcp6_optinfo *));
95
96/* Foxconn added start pling 10/07/2010 */
97/* For testing purpose */
98u_int32_t duid_time = 0;
99/* Foxconn added end pling 10/07/2010 */
100
101/* Foxconn added start pling 09/21/2010 */
102/* Global flags for dhcpc configuration, e.g. IANA_ONLY, IAPD_ONLY */
103static u_int32_t   dhcp6c_flags = 0;
104int set_dhcp6c_flags(u_int32_t flags)
105{
106    dhcp6c_flags |= flags;
107    return 0;
108}
109/* Foxconn added end pling 09/21/2010 */
110
111struct dhcp6_if *
112find_ifconfbyname(const char *ifname)
113{
114	struct dhcp6_if *ifp;
115
116	for (ifp = dhcp6_if; ifp; ifp = ifp->next) {
117		if (strcmp(ifp->ifname, ifname) == 0)
118			return (ifp);
119	}
120
121	return (NULL);
122}
123
124struct dhcp6_if *
125find_ifconfbyid(unsigned int id)
126{
127	struct dhcp6_if *ifp;
128
129	for (ifp = dhcp6_if; ifp; ifp = ifp->next) {
130		if (ifp->ifid == id)
131			return (ifp);
132	}
133
134	return (NULL);
135}
136
137struct host_conf *
138find_hostconf(const struct duid *duid)
139{
140	struct host_conf *host;
141
142	for (host = host_conflist; host; host = host->next) {
143		if (host->duid.duid_len == duid->duid_len &&
144		    memcmp(host->duid.duid_id, duid->duid_id,
145			   host->duid.duid_len) == 0) {
146			return (host);
147		}
148	}
149
150	return (NULL);
151}
152void
153ifinit(const char *ifname)
154{
155	struct dhcp6_if *ifp;
156
157	if ((ifp = find_ifconfbyname(ifname)) != NULL) {
158		dprintf(LOG_NOTICE, "%s" "duplicated interface: %s",
159			FNAME, ifname);
160		return;
161	}
162
163	if ((ifp = malloc(sizeof(*ifp))) == NULL) {
164		dprintf(LOG_ERR, "%s" "malloc failed", FNAME);
165		goto die;
166	}
167	memset(ifp, 0, sizeof(*ifp));
168
169	TAILQ_INIT(&ifp->event_list);
170
171	if ((ifp->ifname = strdup(ifname)) == NULL) {
172		dprintf(LOG_ERR, "%s" "failed to copy ifname", FNAME);
173		goto die;
174	}
175
176	if ((ifp->ifid = if_nametoindex(ifname)) == 0) {
177		dprintf(LOG_ERR, "%s" "invalid interface(%s): %s", FNAME,
178			ifname, strerror(errno));
179		goto die;
180	}
181#ifdef HAVE_SCOPELIB
182	if (inet_zoneid(AF_INET6, 2, ifname, &ifp->linkid)) {
183		dprintf(LOG_ERR, "%s" "failed to get link ID for %s",
184			FNAME, ifname);
185		goto die;
186	}
187#else
188	ifp->linkid = ifp->ifid;
189#endif
190	if (get_linklocal(ifname, &ifp->linklocal) < 0)
191		goto die;
192	ifp->next = dhcp6_if;
193	dhcp6_if = ifp;
194	return;
195
196  die:
197	exit(1);
198}
199
200int
201dhcp6_copy_list(struct dhcp6_list *dst,
202		const struct dhcp6_list *src)
203{
204	const struct dhcp6_listval *ent;
205	struct dhcp6_listval *dent;
206
207	for (ent = TAILQ_FIRST(src); ent; ent = TAILQ_NEXT(ent, link)) {
208		if ((dent = malloc(sizeof(*dent))) == NULL)
209			goto fail;
210
211		memset(dent, 0, sizeof(*dent));
212		memcpy(&dent->uv, &ent->uv, sizeof(ent->uv));
213
214		TAILQ_INSERT_TAIL(dst, dent, link);
215	}
216
217	return 0;
218
219  fail:
220	dhcp6_clear_list(dst);
221	return -1;
222}
223
224void
225dhcp6_clear_list(head)
226	struct dhcp6_list *head;
227{
228	struct dhcp6_listval *v;
229
230	while ((v = TAILQ_FIRST(head)) != NULL) {
231		TAILQ_REMOVE(head, v, link);
232		free(v);
233	}
234
235	return;
236}
237
238void
239relayfree(head)
240	struct relay_list *head;
241{
242	struct relay_listval *v;
243
244	while ((v = TAILQ_FIRST(head)) != NULL) {
245		TAILQ_REMOVE(head, v, link);
246		if (v->intf_id != NULL) {
247			if (v->intf_id->intf_id != NULL)
248				free(v->intf_id->intf_id);
249			free (v->intf_id);
250		}
251		free(v);
252	}
253
254	return;
255}
256
257int
258dhcp6_count_list(head)
259	struct dhcp6_list *head;
260{
261	struct dhcp6_listval *v;
262	int i;
263
264	for (i = 0, v = TAILQ_FIRST(head); v; v = TAILQ_NEXT(v, link))
265		i++;
266
267	return i;
268}
269
270struct dhcp6_listval *
271dhcp6_find_listval(head, val, type)
272	struct dhcp6_list *head;
273	void *val;
274	dhcp6_listval_type_t type;
275{
276	struct dhcp6_listval *lv;
277
278	for (lv = TAILQ_FIRST(head); lv; lv = TAILQ_NEXT(lv, link)) {
279		switch(type) {
280		case DHCP6_LISTVAL_NUM:
281			if (lv->val_num == *(int *)val)
282				return (lv);
283			break;
284		case DHCP6_LISTVAL_ADDR6:
285			if (IN6_ARE_ADDR_EQUAL(&lv->val_addr6,
286			    (struct in6_addr *)val)) {
287				return (lv);
288			}
289			break;
290		case DHCP6_LISTVAL_DHCP6ADDR:
291			if (IN6_ARE_ADDR_EQUAL(&lv->val_dhcp6addr.addr,
292			    &((struct dhcp6_addr *)val)->addr) &&
293			    (lv->val_dhcp6addr.plen == ((struct dhcp6_addr *)val)->plen)) {
294				return (lv);
295			}
296			break;
297		/* DHCP6_LISTVAL_DHCP6LEASE is missing? */
298		}
299
300	}
301
302	return (NULL);
303}
304
305struct dhcp6_listval *
306dhcp6_add_listval(head, val, type)
307	struct dhcp6_list *head;
308	void *val;
309	dhcp6_listval_type_t type;
310{
311	struct dhcp6_listval *lv;
312
313	if ((lv = malloc(sizeof(*lv))) == NULL) {
314		dprintf(LOG_ERR, "%s" "failed to allocate memory for list "
315		    "entry", FNAME);
316		return (NULL);
317	}
318	memset(lv, 0, sizeof(*lv));
319
320	switch(type) {
321	case DHCP6_LISTVAL_NUM:
322		lv->val_num = *(int *)val;
323		break;
324	case DHCP6_LISTVAL_ADDR6:
325		lv->val_addr6 = *(struct in6_addr *)val;
326		break;
327	case DHCP6_LISTVAL_DHCP6ADDR:
328		lv->val_dhcp6addr = *(struct dhcp6_addr *)val;
329		break;
330	default:
331		dprintf(LOG_ERR, "%s" "unexpected list value type (%d)",
332		    FNAME, type);
333		return (NULL);
334	}
335	TAILQ_INSERT_TAIL(head, lv, link);
336	return (lv);
337}
338
339struct dhcp6_event *
340dhcp6_create_event(ifp, state)
341	struct dhcp6_if *ifp;
342	int state;
343{
344	struct dhcp6_event *ev;
345
346	if ((ev = malloc(sizeof(*ev))) == NULL) {
347		dprintf(LOG_ERR, "%s" "failed to allocate memory for an event",
348			FNAME);
349		return (NULL);
350	}
351	/* for safety */
352	memset(ev, 0, sizeof(*ev));
353	ev->serverid.duid_id = NULL;
354
355	ev->ifp = ifp;
356	ev->state = state;
357	TAILQ_INIT(&ev->data_list);
358	dprintf(LOG_DEBUG, "%s" "create an event %p xid %d for state %d",
359		FNAME, ev, ev->xid, ev->state);
360	return (ev);
361}
362
363void
364dhcp6_remove_event(ev)
365	struct dhcp6_event *ev;
366{
367	dprintf(LOG_DEBUG, "%s" "removing an event %p on %s, state=%d, xid=%x", FNAME,
368		ev, ev->ifp->ifname, ev->state, ev->xid);
369
370	if (!TAILQ_EMPTY(&ev->data_list)) {
371		dprintf(LOG_ERR, "%s" "assumption failure: "
372			"event data list is not empty", FNAME);
373		exit(1);
374	}
375	if (ev->serverid.duid_id != NULL)
376		duidfree(&ev->serverid);
377	if (ev->timer)
378		dhcp6_remove_timer(ev->timer);
379	TAILQ_REMOVE(&ev->ifp->event_list, ev, link);
380	free(ev);
381	ev = NULL;
382}
383
384
385int
386getifaddr(addr, ifnam, prefix, plen, strong, ignoreflags)
387	struct in6_addr *addr;
388	char *ifnam;
389	struct in6_addr *prefix;
390	int plen;
391	int strong;		/* if strong host model is required or not */
392	int ignoreflags;
393{
394	struct ifaddrs *ifap, *ifa;
395	struct sockaddr_in6 sin6;
396	int error = -1;
397
398	if (getifaddrs(&ifap) != 0) {
399		err(1, "getifaddr: getifaddrs");
400		/*NOTREACHED*/
401	}
402
403	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
404		int s1, s2;
405
406		if (strong && strcmp(ifnam, ifa->ifa_name) != 0)
407			continue;
408
409		/* in any case, ignore interfaces in different scope zones. */
410		if ((s1 = in6_addrscopebyif(prefix, ifnam)) < 0 ||
411		    (s2 = in6_addrscopebyif(prefix, ifa->ifa_name)) < 0 ||
412		     s1 != s2)
413			continue;
414
415		if (ifa->ifa_addr->sa_family != AF_INET6)
416			continue;
417		if (sizeof(*(ifa->ifa_addr)) > sizeof(sin6))
418			continue;
419
420		if (in6_matchflags(ifa->ifa_addr, ifa->ifa_name, ignoreflags))
421			continue;
422
423		memcpy(&sin6, ifa->ifa_addr, sizeof(sin6));
424#ifdef __KAME__
425		if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
426			sin6.sin6_addr.s6_addr[2] = 0;
427			sin6.sin6_addr.s6_addr[3] = 0;
428		}
429#endif
430		if (plen % 8 == 0) {
431			if (memcmp(&sin6.sin6_addr, prefix, plen / 8) != 0)
432				continue;
433		} else {
434			struct in6_addr a, m;
435			int i;
436
437			memcpy(&a, &sin6.sin6_addr, sizeof(a));
438			memset(&m, 0, sizeof(m));
439			memset(&m, 0xff, plen / 8);
440			m.s6_addr[plen / 8] = (0xff00 >> (plen % 8)) & 0xff;
441			for (i = 0; i < sizeof(a); i++)
442				a.s6_addr[i] &= m.s6_addr[i];
443
444			if (memcmp(&a, prefix, plen / 8) != 0 ||
445			    a.s6_addr[plen / 8] !=
446			    (prefix->s6_addr[plen / 8] & m.s6_addr[plen / 8]))
447				continue;
448		}
449		memcpy(addr, &sin6.sin6_addr, sizeof(*addr));
450#ifdef __KAME__
451		if (IN6_IS_ADDR_LINKLOCAL(addr))
452			addr->s6_addr[2] = addr->s6_addr[3] = 0;
453#endif
454		error = 0;
455		break;
456	}
457
458	freeifaddrs(ifap);
459	return (error);
460}
461
462int
463in6_addrscopebyif(addr, ifnam)
464	struct in6_addr *addr;
465	char *ifnam;
466{
467	u_int ifindex;
468
469	if ((ifindex = if_nametoindex(ifnam)) == 0)
470		return (-1);
471
472	if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
473		return (ifindex);
474
475	if (IN6_IS_ADDR_SITELOCAL(addr) || IN6_IS_ADDR_MC_SITELOCAL(addr))
476		return (1);
477
478	if (IN6_IS_ADDR_MC_ORGLOCAL(addr))
479		return (1);
480
481	return (1);		/* treat it as global */
482}
483
484const char *
485getdev(addr)
486	struct sockaddr_in6 *addr;
487{
488	struct ifaddrs *ifap, *ifa;
489	struct sockaddr_in6 *a6;
490	static char ret_ifname[IFNAMSIZ+1];
491
492	if (getifaddrs(&ifap) != 0) {
493		err(1, "getdev: getifaddrs");
494		/* NOTREACHED */
495	}
496
497	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
498		if (ifa->ifa_addr->sa_family != AF_INET6)
499			continue;
500
501		a6 = (struct sockaddr_in6 *)ifa->ifa_addr;
502		if (!IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &addr->sin6_addr) ||
503		    a6->sin6_scope_id != addr->sin6_scope_id)
504			continue;
505
506		break;
507	}
508
509	if (ifa)
510		strncpy(ret_ifname, ifa->ifa_name, IFNAMSIZ);
511	freeifaddrs(ifap);
512
513	return (ifa ? ret_ifname : NULL);
514}
515
516int
517transmit_sa(s, sa, buf, len)
518	int s;
519	struct sockaddr_in6 *sa;
520	char *buf;
521	size_t len;
522{
523	int error;
524
525	error = sendto(s, buf, len, MSG_DONTROUTE, (struct sockaddr *)sa, sizeof(*sa));
526
527	return (error != len) ? -1 : 0;
528}
529
530long
531random_between(x, y)
532	long x;
533	long y;
534{
535	long ratio;
536
537	ratio = 1 << 16;
538	while ((y - x) * ratio < (y - x))
539		ratio = ratio / 2;
540	return x + ((y - x) * (ratio - 1) / random() & (ratio - 1));
541}
542
543int
544prefix6_mask(in6, plen)
545	struct in6_addr *in6;
546	int plen;
547{
548	struct sockaddr_in6 mask6;
549	int i;
550
551	if (sa6_plen2mask(&mask6, plen))
552		return (-1);
553
554	for (i = 0; i < 16; i++)
555		in6->s6_addr[i] &= mask6.sin6_addr.s6_addr[i];
556
557	return (0);
558}
559
560int
561sa6_plen2mask(sa6, plen)
562	struct sockaddr_in6 *sa6;
563	int plen;
564{
565	u_char *cp;
566
567	if (plen < 0 || plen > 128)
568		return (-1);
569
570	memset(sa6, 0, sizeof(*sa6));
571	sa6->sin6_family = AF_INET6;
572
573	for (cp = (u_char *)&sa6->sin6_addr; plen > 7; plen -= 8)
574		*cp++ = 0xff;
575	*cp = 0xff << (8 - plen);
576
577	return (0);
578}
579
580char *
581addr2str(sa)
582	struct sockaddr *sa;
583{
584	static char addrbuf[8][NI_MAXHOST];
585	static int round = 0;
586	char *cp;
587
588	round = (round + 1) & 7;
589	cp = addrbuf[round];
590
591	if (getnameinfo(sa, NI_MAXSERV, cp, NI_MAXHOST, NULL,
592				0, NI_NUMERICHOST) != 0)
593		dprintf(LOG_ERR, "%s getnameinfo return error", FNAME);
594
595	return (cp);
596}
597
598char *
599in6addr2str(in6, scopeid)
600	struct in6_addr *in6;
601	int scopeid;
602{
603	struct sockaddr_in6 sa6;
604
605	memset(&sa6, 0, sizeof(sa6));
606	sa6.sin6_family = AF_INET6;
607	sa6.sin6_addr = *in6;
608	sa6.sin6_scope_id = scopeid;
609
610	return (addr2str((struct sockaddr *)&sa6));
611}
612
613/* return IPv6 address scope type. caller assumes that smaller is narrower. */
614int
615in6_scope(addr)
616	struct in6_addr *addr;
617{
618	int scope;
619
620	if (addr->s6_addr[0] == 0xfe) {
621		scope = addr->s6_addr[1] & 0xc0;
622
623		switch (scope) {
624		case 0x80:
625			return 2; /* link-local */
626			break;
627		case 0xc0:
628			return 5; /* site-local */
629			break;
630		default:
631			return 14; /* global: just in case */
632			break;
633		}
634	}
635
636	/* multicast scope. just return the scope field */
637	if (addr->s6_addr[0] == 0xff)
638		return (addr->s6_addr[1] & 0x0f);
639
640	if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) {
641		if (addr->s6_addr[15] == 1) /* loopback */
642			return 1;
643		if (addr->s6_addr[15] == 0) /* unspecified */
644			return 0;
645	}
646
647	return 14;		/* global */
648}
649
650static int
651in6_matchflags(addr, ifnam, flags)
652	struct sockaddr *addr;
653	char *ifnam;
654	int flags;
655{
656	int s;
657	struct ifreq ifr;
658
659	if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
660		warn("in6_matchflags: socket(DGRAM6)");
661		return (-1);
662	}
663	memset(&ifr, 0, sizeof(ifr));
664	strncpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
665	ifr.ifr_addr = *(struct sockaddr *)addr;
666
667	if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
668		warn("in6_matchflags: ioctl(SIOCGIFFLAGS, %s)",
669		     addr2str(addr));
670		close(s);
671		return (-1);
672	}
673
674	close(s);
675
676	return (ifr.ifr_ifru.ifru_flags & flags);
677}
678
679int
680configure_duid(const char *str,
681	       struct duid *duid)
682{
683	const char *cp;
684	char  *bp, *idbuf = NULL;
685	int duidlen, slen;
686	unsigned int x;
687
688	/* calculate DUID len */
689	slen = strlen(str);
690	if (slen < 2)
691		goto bad;
692	duidlen = 1;
693	slen -= 2;
694	if ((slen % 3) != 0)
695		goto bad;
696	duidlen += (slen / 3);
697	if (duidlen > 256) {
698		dprintf(LOG_ERR, "%s" "too long DUID (%d)", FNAME, duidlen);
699		return (-1);
700	}
701
702	if ((idbuf = (char *)malloc(duidlen)) == NULL) {
703		dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME);
704		return (-1);
705	}
706
707	for (cp = str, bp = idbuf; *cp;) {
708		if (*cp == ':') {
709			cp++;
710			continue;
711		}
712
713		if (sscanf(cp, "%02x", &x) != 1)
714			goto bad;
715		*bp = x;
716		cp += 2;
717		bp++;
718	}
719
720	duid->duid_len = duidlen;
721	duid->duid_id = idbuf;
722	dprintf(LOG_DEBUG, "configure duid is %s", duidstr(duid));
723	return (0);
724
725  bad:
726	if (idbuf)
727		free(idbuf);
728	dprintf(LOG_ERR, "%s" "assumption failure (bad string)", FNAME);
729	return (-1);
730}
731
732int
733get_duid(const 	char *idfile, const char *ifname,
734	 struct duid *duid)
735{
736	FILE *fp = NULL;
737	u_int16_t len = 0, hwtype;
738	struct dhcp6_duid_type1 *dp; /* we only support the type1 DUID */
739	char tmpbuf[256];	/* DUID should be no more than 256 bytes */
740
741	if ((fp = fopen(idfile, "r")) == NULL && errno != ENOENT)
742		dprintf(LOG_NOTICE, "%s" "failed to open DUID file: %s",
743		    FNAME, idfile);
744
745	if (fp) {
746		/* decode length */
747		if (fread(&len, sizeof(len), 1, fp) != 1) {
748			dprintf(LOG_ERR, "%s" "DUID file corrupted", FNAME);
749			goto fail;
750		}
751	} else {
752		int l;
753
754		if ((l = gethwid(tmpbuf, sizeof(tmpbuf), ifname, &hwtype)) < 0) {
755			dprintf(LOG_INFO, "%s"
756			    "failed to get a hardware address", FNAME);
757			goto fail;
758		}
759		len = l + sizeof(struct dhcp6_duid_type1);
760	}
761
762	memset(duid, 0, sizeof(*duid));
763	duid->duid_len = len;
764	if ((duid->duid_id = (char *)malloc(len)) == NULL) {
765		dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
766		goto fail;
767	}
768
769	/* copy (and fill) the ID */
770	if (fp) {
771		if (fread(duid->duid_id, len, 1, fp) != 1) {
772			dprintf(LOG_ERR, "%s" "DUID file corrupted", FNAME);
773			goto fail;
774		}
775
776		dprintf(LOG_DEBUG, "%s"
777		    "extracted an existing DUID from %s: %s", FNAME,
778		    idfile, duidstr(duid));
779	} else {
780		u_int64_t t64;
781
782		dp = (struct dhcp6_duid_type1 *)duid->duid_id;
783		/* Foxconn modifed start pling 04/26/2011 */
784		/* Netgear Router Spec requires DUID to be type DUID-LL, not DUID-LLT */
785		/* dp->dh6duid1_type = htons(1); */ /* type 1 */
786		dp->dh6duid1_type = htons(3); /* type 3: DUID-LL */
787		/* Foxconn modifed end pling 04/26/2011 */
788		dp->dh6duid1_hwtype = htons(hwtype);
789		/* time is Jan 1, 2000 (UTC), modulo 2^32 */
790		t64 = (u_int64_t)(time(NULL) - 946684800);
791        /* Foxconn added start pling 10/07/2010 */
792        /* For testing purposes !!! */
793        if (duid_time) {
794            dprintf(LOG_DEBUG, "%s"
795                "**TESTING** Use user-defined duid_time %lu", FNAME, duid_time);
796            t64 = (u_int64_t)duid_time;
797        }
798        /* Foxconn added end pling 10/07/2010 */
799		/* Foxconn removed start pling 04/26/2011 */
800		/* Netgear Router Spec requires DUID to be type DUID-LL, not DUID-LLT */
801		/* dp->dh6duid1_time = htonl((u_long)(t64 & 0xffffffff)); */
802		/* Foxconn removed end pling 04/26/2011 */
803		memcpy((void *)(dp + 1), tmpbuf, (len - sizeof(*dp)));
804
805		dprintf(LOG_DEBUG, "%s" "generated a new DUID: %s", FNAME,
806			duidstr(duid));
807	}
808
809	/* save the (new) ID to the file for next time */
810	if (!fp) {
811		if ((fp = fopen(idfile, "w+")) == NULL) {
812			dprintf(LOG_ERR, "%s"
813			    "failed to open DUID file for save", FNAME);
814			goto fail;
815		}
816		if ((fwrite(&len, sizeof(len), 1, fp)) != 1) {
817			dprintf(LOG_ERR, "%s" "failed to save DUID", FNAME);
818			goto fail;
819		}
820		if ((fwrite(duid->duid_id, len, 1, fp)) != 1) {
821			dprintf(LOG_ERR, "%s" "failed to save DUID", FNAME);
822			goto fail;
823		}
824
825		dprintf(LOG_DEBUG, "%s" "saved generated DUID to %s", FNAME,
826			idfile);
827	}
828
829	if (fp)
830		fclose(fp);
831	return (0);
832
833  fail:
834	if (fp)
835		fclose(fp);
836	if (duid->duid_id != NULL) {
837		duidfree(duid);
838	}
839	return (-1);
840}
841
842ssize_t
843gethwid(buf, len, ifname, hwtypep)
844	char *buf;
845	int len;
846	const char *ifname;
847	u_int16_t *hwtypep;
848{
849	int skfd;
850	ssize_t l;
851	struct ifreq if_hwaddr;
852
853	if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0 )) < 0)
854		return -1;
855
856	strcpy(if_hwaddr.ifr_name, ifname);
857	if (ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr) < 0)
858		return -1;
859	/* only support Ethernet */
860	switch (if_hwaddr.ifr_hwaddr.sa_family) {
861	case ARPHRD_ETHER:
862	case ARPHRD_IEEE802:
863		*hwtypep = ARPHRD_ETHER;
864		l = 6;
865		break;
866	case ARPHRD_PPP:
867		*hwtypep = ARPHRD_PPP;
868		l = 0;
869		return l;
870	default:
871		dprintf(LOG_INFO, "dhcpv6 doesn't support hardware type %d",
872			if_hwaddr.ifr_hwaddr.sa_family);
873		return -1;
874	}
875	memcpy(buf, if_hwaddr.ifr_hwaddr.sa_data, l);
876	dprintf(LOG_DEBUG, "%s found an interface %s hardware %p",
877		FNAME, ifname, buf);
878	return l;
879}
880
881void
882dhcp6_init_options(optinfo)
883	struct dhcp6_optinfo *optinfo;
884{
885	memset(optinfo, 0, sizeof(*optinfo));
886	/* for safety */
887	optinfo->clientID.duid_id = NULL;
888	optinfo->serverID.duid_id = NULL;
889	optinfo->pref = DH6OPT_PREF_UNDEF;
890	TAILQ_INIT(&optinfo->addr_list);
891	/* Foxconn added start pling 09/23/2009 */
892	TAILQ_INIT(&optinfo->prefix_list);
893	/* Foxconn added end pling 09/23/2009 */
894	TAILQ_INIT(&optinfo->reqopt_list);
895	TAILQ_INIT(&optinfo->stcode_list);
896	TAILQ_INIT(&optinfo->dns_list.addrlist);
897    /* Foxconn added start pling 01/25/2010 */
898	TAILQ_INIT(&optinfo->sip_list);
899	TAILQ_INIT(&optinfo->ntp_list);
900    /* Foxconn added end pling 01/25/2010 */
901	TAILQ_INIT(&optinfo->relay_list);
902	optinfo->dns_list.domainlist = NULL;
903}
904
905void
906dhcp6_clear_options(optinfo)
907	struct dhcp6_optinfo *optinfo;
908{
909	struct domain_list *dlist, *dlist_next;
910	duidfree(&optinfo->clientID);
911	duidfree(&optinfo->serverID);
912
913	dhcp6_clear_list(&optinfo->addr_list);
914	/* Foxconn added start pling 09/23/2009 */
915	dhcp6_clear_list(&optinfo->prefix_list);
916	/* Foxconn added end pling 09/23/2009 */
917	dhcp6_clear_list(&optinfo->reqopt_list);
918	dhcp6_clear_list(&optinfo->stcode_list);
919	dhcp6_clear_list(&optinfo->dns_list.addrlist);
920	relayfree(&optinfo->relay_list);
921	if (dhcp6_mode == DHCP6_MODE_CLIENT) {
922		for (dlist = optinfo->dns_list.domainlist; dlist; dlist = dlist_next) {
923			dlist_next = dlist->next;
924			free(dlist);
925		}
926	}
927	optinfo->dns_list.domainlist = NULL;
928	dhcp6_init_options(optinfo);
929}
930
931int
932dhcp6_copy_options(dst, src)
933	struct dhcp6_optinfo *dst, *src;
934{
935	if (duidcpy(&dst->clientID, &src->clientID))
936		goto fail;
937	if (duidcpy(&dst->serverID, &src->serverID))
938		goto fail;
939	dst->flags = src->flags;
940
941	if (dhcp6_copy_list(&dst->addr_list, &src->addr_list))
942		goto fail;
943	/* Foxconn added start pling 09/23/2009 */
944	if (dhcp6_copy_list(&dst->prefix_list, &src->prefix_list))
945		goto fail;
946	/* Foxconn added end pling 09/23/2009 */
947	if (dhcp6_copy_list(&dst->reqopt_list, &src->reqopt_list))
948		goto fail;
949	if (dhcp6_copy_list(&dst->stcode_list, &src->stcode_list))
950		goto fail;
951	if (dhcp6_copy_list(&dst->dns_list.addrlist, &src->dns_list.addrlist))
952		goto fail;
953	memcpy(&dst->server_addr, &src->server_addr, sizeof(dst->server_addr));
954	dst->pref = src->pref;
955
956	return 0;
957
958  fail:
959	/* cleanup temporary resources */
960	dhcp6_clear_options(dst);
961	return -1;
962}
963
964/* Foxconn added start pling 10/04/2010 */
965/* Add two extra arguments for DHCP client to use.
966 * These two args are ignored in DHCP server mode (currently)
967 */
968#if 0
969int
970dhcp6_get_options(p, ep, optinfo)
971	struct dhcp6opt *p, *ep;
972	struct dhcp6_optinfo *optinfo;
973#endif
974int
975dhcp6_get_options(p, ep, optinfo, msgtype, state, send_flags)
976    struct dhcp6opt *p, *ep;
977    struct dhcp6_optinfo *optinfo;
978    int msgtype, state, send_flags;
979/* Foxconn added end pling 10/04/2010 */
980{
981	struct dhcp6opt *np, opth;
982	int i, opt, optlen, reqopts, num;
983	char *cp, *val;
984	u_int16_t val16;
985
986    /* Foxconn added start pling 09/24/2009 */
987    int has_iana = 0;
988    int has_iapd = 0;
989    /* Foxconn added end pling 09/24/2009 */
990    /* Foxconn added start pling 10/04/2010 */
991    int has_dns = 0;
992    int has_ntp = 0;
993    int has_sip = 0;
994    /* Foxconn added end pling 10/04/2010 */
995    /* Foxconn added start pling 01/25/2010 */
996    char buf[1204];
997    char tmp_buf[1024];
998    char command[1024];
999    /* Foxconn added end pling 01/25/2010 */
1000    int  type_set = 0;      // pling added 10/22/2010
1001
1002	for (; p + 1 <= ep; p = np) {
1003		struct duid duid0;
1004
1005		memcpy(&opth, p, sizeof(opth));
1006		optlen = ntohs(opth.dh6opt_len);
1007		opt = ntohs(opth.dh6opt_type);
1008
1009		cp = (char *)(p + 1);
1010		np = (struct dhcp6opt *)(cp + optlen);
1011
1012		dprintf(LOG_DEBUG, "%s" "get DHCP option %s, len %d",
1013		    FNAME, dhcp6optstr(opt), optlen);
1014
1015		/* option length field overrun */
1016		if (np > ep) {
1017			dprintf(LOG_INFO,
1018			    "%s" "malformed DHCP options", FNAME);
1019			return -1;
1020		}
1021
1022		switch (opt) {
1023		case DH6OPT_CLIENTID:
1024			if (optlen == 0)
1025				goto malformed;
1026			duid0.duid_len = optlen;
1027			duid0.duid_id = cp;
1028			dprintf(LOG_DEBUG, "  DUID: %s", duidstr(&duid0));
1029			if (duidcpy(&optinfo->clientID, &duid0)) {
1030				dprintf(LOG_ERR, "%s" "failed to copy DUID",
1031					FNAME);
1032				goto fail;
1033			}
1034			break;
1035		case DH6OPT_SERVERID:
1036			if (optlen == 0)
1037				goto malformed;
1038			duid0.duid_len = optlen;
1039			duid0.duid_id = cp;
1040			dprintf(LOG_DEBUG, "  DUID: %s", duidstr(&duid0));
1041			if (duidcpy(&optinfo->serverID, &duid0)) {
1042				dprintf(LOG_ERR, "%s" "failed to copy DUID",
1043					FNAME);
1044				goto fail;
1045			}
1046			break;
1047		case DH6OPT_ELAPSED_TIME:
1048			if (optlen != sizeof(u_int16_t))
1049				goto malformed;
1050			memcpy(&val16, cp, sizeof(val16));
1051			num = ntohs(val16);
1052			dprintf(LOG_DEBUG, " this message elapsed time is: %d",
1053				num);
1054			break;
1055		case DH6OPT_STATUS_CODE:
1056			if (optlen < sizeof(u_int16_t))
1057				goto malformed;
1058			memcpy(&val16, cp, sizeof(val16));
1059			num = ntohs(val16);
1060			dprintf(LOG_DEBUG, "  this message status code: %s",
1061			    dhcp6_stcodestr(num));
1062
1063
1064			/* need to check duplication? */
1065
1066			if (dhcp6_add_listval(&optinfo->stcode_list,
1067			    &num, DHCP6_LISTVAL_NUM) == NULL) {
1068				dprintf(LOG_ERR, "%s" "failed to copy "
1069				    "status code", FNAME);
1070				goto fail;
1071			}
1072
1073			break;
1074		case DH6OPT_ORO:
1075			if ((optlen % 2) != 0 || optlen == 0)
1076				goto malformed;
1077			reqopts = optlen / 2;
1078			for (i = 0, val = cp; i < reqopts;
1079			     i++, val += sizeof(u_int16_t)) {
1080				u_int16_t opttype;
1081
1082				memcpy(&opttype, val, sizeof(u_int16_t));
1083				num = ntohs(opttype);
1084
1085				dprintf(LOG_DEBUG, "  requested option: %s",
1086					dhcp6optstr(num));
1087
1088				if (dhcp6_find_listval(&optinfo->reqopt_list,
1089				    &num, DHCP6_LISTVAL_NUM)) {
1090					dprintf(LOG_INFO, "%s" "duplicated "
1091					    "option type (%s)", FNAME,
1092					    dhcp6optstr(opttype));
1093					goto nextoption;
1094				}
1095
1096				if (dhcp6_add_listval(&optinfo->reqopt_list,
1097				    &num, DHCP6_LISTVAL_NUM) == NULL) {
1098					dprintf(LOG_ERR, "%s" "failed to copy "
1099					    "requested option", FNAME);
1100					goto fail;
1101				}
1102			nextoption: ;
1103			}
1104			break;
1105		case DH6OPT_PREFERENCE:
1106			if (optlen != 1)
1107				goto malformed;
1108			optinfo->pref = (u_int8_t)*(u_char *)cp;
1109			dprintf(LOG_DEBUG, "%s" "get option preferrence is %2x",
1110					FNAME, optinfo->pref);
1111			break;
1112		case DH6OPT_RAPID_COMMIT:
1113			if (optlen != 0)
1114				goto malformed;
1115			optinfo->flags |= DHCIFF_RAPID_COMMIT;
1116			break;
1117		case DH6OPT_UNICAST:
1118			if (optlen != sizeof(struct in6_addr)
1119			    && dhcp6_mode != DHCP6_MODE_CLIENT)
1120				goto malformed;
1121			optinfo->flags |= DHCIFF_UNICAST;
1122			memcpy(&optinfo->server_addr,
1123			       (struct in6_addr *)cp, sizeof(struct in6_addr));
1124			break;
1125		case DH6OPT_IA_TA:
1126			if (optlen < sizeof(u_int32_t))
1127				goto malformed;
1128			/* check iaid */
1129			optinfo->flags |= DHCIFF_TEMP_ADDRS;
1130			optinfo->type = IATA;
1131			dprintf(LOG_DEBUG, "%s" "get option iaid is %u",
1132				FNAME, optinfo->iaidinfo.iaid);
1133			optinfo->iaidinfo.iaid = ntohl(*(u_int32_t *)cp);
1134			if (get_assigned_ipv6addrs(cp + 4, cp + optlen, optinfo))
1135				goto fail;
1136			break;
1137		case DH6OPT_IA_NA:
1138		case DH6OPT_IA_PD:
1139            /* Foxconn modified start pling 09/23/2009 */
1140			if (dhcp6_mode == DHCP6_MODE_SERVER ||
1141                (dhcp6_mode == DHCP6_MODE_CLIENT && (send_flags & DHCIFF_SOLICIT_ONLY)) ||
1142                (dhcp6_mode == DHCP6_MODE_CLIENT && (dhcp6c_flags & DHCIFF_IAPD_ONLY))) {
1143                /* pling modified start 10/22/2010 */
1144                /* For each packet, we set to one type only (IANA/IAPD)
1145                 * but not both.
1146                 */
1147    			if (opt == DH6OPT_IA_NA && !type_set)
1148                {
1149	    			optinfo->type = IANA;
1150                    type_set = 1;
1151                }
1152			    else if (opt == DH6OPT_IA_PD && !type_set)
1153                {
1154				    optinfo->type = IAPD;
1155                    type_set = 1;
1156                }
1157                /* pling modified end 10/22/2010 */
1158            } else {
1159                /* don't set optinfo->type to IAPD as this version
1160                 * of dhcp6c can't handle IANA and IAPD concurrently.
1161                 */
1162    			if (opt == DH6OPT_IA_NA)
1163	    			optinfo->type = IANA;
1164            }
1165            /* Foxconn modified end pling 09/23/2009 */
1166			/* check iaid */
1167			if (optlen < sizeof(struct dhcp6_iaid_info))
1168				goto malformed;
1169			if (dhcp6_mode == DHCP6_MODE_CLIENT && opt == DH6OPT_IA_PD)
1170                ;// If this is IAPD, don't modify the IAID
1171            else
1172    			optinfo->iaidinfo.iaid = ntohl(*(u_int32_t *)cp);
1173			optinfo->iaidinfo.renewtime =
1174				ntohl(*(u_int32_t *)(cp + sizeof(u_int32_t)));
1175			optinfo->iaidinfo.rebindtime =
1176				ntohl(*(u_int32_t *)(cp + 2 * sizeof(u_int32_t)));
1177			dprintf(LOG_DEBUG, "get option iaid is %u, renewtime %u, "
1178				"rebindtime %u", optinfo->iaidinfo.iaid,
1179				optinfo->iaidinfo.renewtime, optinfo->iaidinfo.rebindtime);
1180            /* Foxconn added start pling 10/07/2010 */
1181            /* DHCPv6 client readylogo:
1182             * Ignore IA with T1 > T2 */
1183            if (optinfo->iaidinfo.renewtime > optinfo->iaidinfo.rebindtime)
1184                goto fail;
1185            /* Foxconn added end pling 10/07/2010 */
1186			if (get_assigned_ipv6addrs(cp + 3 * sizeof(u_int32_t),
1187						cp + optlen, optinfo))
1188				goto fail;
1189
1190            /* Foxconn added start pling 09/24/2009 */
1191			if (dhcp6_mode == DHCP6_MODE_CLIENT) {
1192			    if (opt == DH6OPT_IA_NA)
1193                    has_iana = 1;
1194                else
1195                    has_iapd = 1;
1196            }
1197            /* Foxconn added end pling 09/24/2009 */
1198			break;
1199		case DH6OPT_DNS_SERVERS:
1200			if (optlen % sizeof(struct in6_addr) || optlen == 0)
1201				goto malformed;
1202			for (val = cp; val < cp + optlen;
1203			     val += sizeof(struct in6_addr)) {
1204				if (dhcp6_find_listval(&optinfo->dns_list.addrlist,
1205				    val, DHCP6_LISTVAL_ADDR6)) {
1206					dprintf(LOG_INFO, "%s" "duplicated "
1207					    "DNS address (%s)", FNAME,
1208					    in6addr2str((struct in6_addr *)val,
1209						0));
1210					goto nextdns;
1211				}
1212
1213				if (dhcp6_add_listval(&optinfo->dns_list.addrlist,
1214				    val, DHCP6_LISTVAL_ADDR6) == NULL) {
1215					dprintf(LOG_ERR, "%s" "failed to copy "
1216					    "DNS address", FNAME);
1217					goto fail;
1218				}
1219			nextdns: ;
1220			}
1221            /* Foxconn added start pling 10/04/2010 */
1222            if (dhcp6_mode == DHCP6_MODE_CLIENT)
1223                has_dns = 1;
1224            /* Foxconn added end pling 10/04/2010 */
1225			break;
1226
1227        /* Foxconn added start pling 01/25/2010 */
1228        case DH6OPT_SIP_SERVERS:
1229            memset(buf, 0, sizeof(buf));
1230            memset(tmp_buf, 0, sizeof(tmp_buf));
1231            if (optlen % sizeof(struct in6_addr) || optlen == 0)
1232                goto malformed;
1233            for (val = cp; val < cp + optlen;
1234                 val += sizeof(struct in6_addr)) {
1235                if (dhcp6_find_listval(&optinfo->sip_list,
1236                    val, DHCP6_LISTVAL_ADDR6)) {
1237                    dprintf(LOG_INFO, "%s" "duplicated "
1238                        "SIP address (%s)", FNAME,
1239                        in6addr2str((struct in6_addr *)val,
1240                        0));
1241                    goto nextsip;
1242                }
1243
1244                if (dhcp6_add_listval(&optinfo->sip_list,
1245                    val, DHCP6_LISTVAL_ADDR6) == NULL) {
1246                        dprintf(LOG_ERR, "%s" "failed to copy "
1247                        "SIP address", FNAME);
1248                    goto fail;
1249                }
1250                /* Save SIP server to NVRAM */
1251                sprintf(tmp_buf, "%s ", in6addr2str((struct in6_addr *)val, 0));
1252                strcat(buf, tmp_buf);
1253            nextsip: ;
1254            }
1255            /* Save SIP server to NVRAM */
1256            if (dhcp6_mode == DHCP6_MODE_CLIENT && strlen(buf)) {
1257                sprintf(command, "nvram set ipv6_sip_servers=\"%s\"", buf);
1258                system(command);
1259                has_sip = 1;    // Foxconn added pling 10/04/2010
1260            }
1261            break;
1262		case DH6OPT_NTP_SERVERS:
1263            memset(buf, 0, sizeof(buf));
1264            memset(tmp_buf, 0, sizeof(tmp_buf));
1265			if (optlen % sizeof(struct in6_addr) || optlen == 0)
1266				goto malformed;
1267			for (val = cp; val < cp + optlen;
1268			     val += sizeof(struct in6_addr)) {
1269				if (dhcp6_find_listval(&optinfo->ntp_list,
1270				    val, DHCP6_LISTVAL_ADDR6)) {
1271					dprintf(LOG_INFO, "%s" "duplicated "
1272					    "NTP address (%s)", FNAME,
1273					    in6addr2str((struct in6_addr *)val,
1274						0));
1275					goto nextntp;
1276				}
1277
1278				if (dhcp6_add_listval(&optinfo->ntp_list,
1279				    val, DHCP6_LISTVAL_ADDR6) == NULL) {
1280					dprintf(LOG_ERR, "%s" "failed to copy "
1281					    "NTP address", FNAME);
1282					goto fail;
1283				}
1284                /* Save SIP server to NVRAM */
1285                sprintf(tmp_buf, "%s ", in6addr2str((struct in6_addr *)val, 0));
1286                strcat(buf, tmp_buf);
1287			nextntp: ;
1288			}
1289            /* Save NTP server to NVRAM */
1290            if (dhcp6_mode == DHCP6_MODE_CLIENT && strlen(buf)) {
1291                sprintf(command, "nvram set ipv6_ntp_servers=\"%s\"", buf);
1292                system(command);
1293                has_ntp = 1;    // Foxconn added pling 10/04/2010
1294            }
1295			break;
1296        /* Foxconn added end pling 01/25/2010 */
1297
1298		case DH6OPT_DOMAIN_LIST:
1299			if (optlen == 0)
1300				goto malformed;
1301			/* dependency on lib resolv */
1302			for (val = cp; val < cp + optlen;) {
1303				int n;
1304				struct domain_list *dname, *dlist;
1305				dname = malloc(sizeof(*dname));
1306				if (dname == NULL) {
1307					dprintf(LOG_ERR, "%s" "failed to allocate memory",
1308						FNAME);
1309					goto fail;
1310				}
1311				n =  dn_expand(cp, cp + optlen, val, dname->name, MAXDNAME);
1312				if (n < 0)
1313					goto malformed;
1314				else {
1315					val += n;
1316					dprintf(LOG_DEBUG, "expand domain name %s, size %d",
1317						dname->name, strlen(dname->name));
1318				}
1319				dname->next = NULL;
1320				if (optinfo->dns_list.domainlist == NULL) {
1321					optinfo->dns_list.domainlist = dname;
1322				} else {
1323					for (dlist = optinfo->dns_list.domainlist; dlist;
1324					     dlist = dlist->next) {
1325						if (dlist->next == NULL) {
1326							dlist->next = dname;
1327							break;
1328						}
1329					}
1330				}
1331			}
1332			break;
1333		default:
1334			/* no option specific behavior */
1335			dprintf(LOG_INFO, "%s"
1336			    "unknown or unexpected DHCP6 option %s, len %d",
1337			    FNAME, dhcp6optstr(opt), optlen);
1338			break;
1339		}
1340	}
1341
1342    /* Foxconn added start pling 09/24/2009 */
1343    /* Per Netgear spec, an acceptable DHCP advertise
1344     *  must have both IANA and IAPD option.
1345     */
1346    if (dhcp6_mode == DHCP6_MODE_CLIENT) {
1347        /* Foxconn added start pling 09/21/2010 */
1348        /* Check flag to see if we accept IANA/IAPD only
1349         *  for DHCPv6 readylogo test.
1350         */
1351        if ((dhcp6c_flags & DHCIFF_IANA_ONLY) && has_iana)
1352        {
1353            dprintf(LOG_INFO, "%s" "recv IANA. OK!", FNAME);
1354        }
1355        else
1356        if (dhcp6c_flags & DHCIFF_INFO_ONLY)
1357        {
1358            dprintf(LOG_INFO, "%s" "Info-only. OK!", FNAME);
1359        }
1360        else
1361        if  ((dhcp6c_flags & DHCIFF_IAPD_ONLY) && has_iapd)
1362        {
1363            dprintf(LOG_INFO, "%s" "recv IAPD. OK!", FNAME);
1364        }
1365        else
1366        /* Foxconn added end pling 09/21/2010 */
1367        /* Foxconn added start pling 10/04/2010 */
1368        /* Handle DHCP messages properly in different states */
1369        if (state == DHCP6S_INFOREQ && msgtype == DH6_REPLY &&
1370            has_dns && has_ntp && has_sip)
1371        {
1372            dprintf(LOG_INFO, "%s" "valid INFOREQ/REPLY. OK!", FNAME);
1373        }
1374        else
1375        if (state == DHCP6S_DECLINE && msgtype == DH6_REPLY)
1376        {
1377            dprintf(LOG_INFO, "%s" "got REPLY to DECLINE.", FNAME);
1378        }
1379        else
1380        /* Foxconn added end pling 10/04/2010 */
1381        /* Foxconn added start pling 09/16/2011 */
1382        /* In auto-detect mode, we don't accept Advert pkt with IANA only. */
1383        if ((send_flags & DHCIFF_SOLICIT_ONLY) && has_iana && !has_iapd)
1384        {
1385            dprintf(LOG_INFO, "%s" "got IANA only in auto-detect mode. NG!", FNAME);
1386            goto fail;
1387        }
1388        else
1389        /* Foxconn added end pling 09/16/2011 */
1390        /* Foxconn added start pling 10/14/2010 */
1391        if ((send_flags & DHCIFF_SOLICIT_ONLY) &&
1392            (has_iana || has_iapd) )
1393        {
1394            dprintf(LOG_INFO, "%s" "got IANA/IAPD in auto-detect mode", FNAME);
1395        }
1396        else
1397        /* Foxconn added end pling 10/14/2010 */
1398        if (!has_iana || !has_iapd) {
1399            dprintf(LOG_INFO, "%s" "no IANA/IAPD", FNAME);
1400            goto fail;
1401        }
1402    }
1403    /* Foxconn added end pling 09/24/2009 */
1404
1405	return (0);
1406
1407  malformed:
1408	dprintf(LOG_INFO, "%s" "malformed DHCP option: type %d, len %d",
1409	    FNAME, opt, optlen);
1410  fail:
1411	dhcp6_clear_options(optinfo);
1412	return (-1);
1413}
1414
1415static int
1416get_assigned_ipv6addrs(p, ep, optinfo)
1417	char *p, *ep;
1418	struct dhcp6_optinfo *optinfo;
1419{
1420	char *np, *cp;
1421	struct dhcp6opt opth;
1422	struct dhcp6_addr_info ai;
1423	struct dhcp6_prefix_info pi;
1424	struct dhcp6_addr addr6;
1425	int optlen, opt;
1426	u_int16_t val16;
1427	int num;
1428    int has_status_code = 0;    /* Foxconn added pling 09/15/2011 */
1429
1430	/* Foxconn added start pling 12/22/2011 */
1431	char iapd_valid_lifetime_cmd_buf[1024];
1432	char iapd_preferred_lifetime_cmd_buf[1024];
1433	/* Foxconn added end pling 12/22/2011 */
1434
1435    /* Foxconn modified start pling 09/15/2011 */
1436    /* To work around IANA/IAPD without status code */
1437	//for (; p + sizeof(struct dhcp6opt) <= ep; p = np) {
1438	for (; /*p + sizeof(struct dhcp6opt) <= ep*/; p = np) {
1439
1440        if (p + sizeof(struct dhcp6opt) > ep)
1441        {
1442            /* Foxconn added start pling 10/19/2011 */
1443            /* for server, use original logic (break for loop) */
1444            if (dhcp6_mode == DHCP6_MODE_SERVER)
1445                break;
1446            /* Foxconn added end pling 10/19/2011 */
1447
1448            /* Client check status code below */
1449            if (has_status_code)
1450                break;
1451            else {
1452                has_status_code = 1;
1453                goto no_status_code;
1454            }
1455        }
1456    /* Foxconn modified end pling 09/15/2011 */
1457		memcpy(&opth, p, sizeof(opth));
1458		optlen =  ntohs(opth.dh6opt_len);
1459		opt = ntohs(opth.dh6opt_type);
1460		cp = p + sizeof(opth);
1461		np = cp + optlen;
1462		dprintf(LOG_DEBUG, "  IA address option: %s, "
1463			"len %d", dhcp6optstr(opt), optlen);
1464
1465		if (np > ep) {
1466			dprintf(LOG_INFO, "%s" "malformed DHCP options",
1467			    FNAME);
1468			return -1;
1469		}
1470		switch(opt) {
1471		case DH6OPT_STATUS_CODE:
1472			if (optlen < sizeof(val16))
1473				goto malformed;
1474			memcpy(&val16, cp, sizeof(val16));
1475			num = ntohs(val16);
1476			dprintf(LOG_INFO, "status code for this address is: %s",
1477				dhcp6_stcodestr(num));
1478			if (optlen > sizeof(val16)) {
1479				dprintf(LOG_INFO,
1480					"status message for this address is: %-*s",
1481					(int)(optlen-sizeof(val16)), p+(val16));
1482			}
1483			if (dhcp6_add_listval(&optinfo->stcode_list,
1484			    &num, DHCP6_LISTVAL_NUM) == NULL) {
1485				dprintf(LOG_ERR, "%s" "failed to copy "
1486				    "status code", FNAME);
1487				goto fail;
1488			}
1489            has_status_code = 1;    /* Foxconn added pling 09/15/2011 */
1490			break;
1491		case DH6OPT_IADDR:
1492			if (optlen < sizeof(ai) - sizeof(u_int32_t))
1493				goto malformed;
1494			memcpy(&ai, p, sizeof(ai));
1495			/* copy the information into internal format */
1496			memset(&addr6, 0, sizeof(addr6));
1497			memcpy(&addr6.addr, (struct in6_addr *)cp, sizeof(struct in6_addr));
1498			addr6.preferlifetime = ntohl(ai.preferlifetime);
1499			addr6.validlifetime = ntohl(ai.validlifetime);
1500			dprintf(LOG_DEBUG, "  get IAADR address information: "
1501			    "%s preferlifetime %d validlifetime %d",
1502			    in6addr2str(&addr6.addr, 0),
1503			    addr6.preferlifetime, addr6.validlifetime);
1504			/* It shouldn't happen, since Server will do the check before
1505			 * sending the data to clients */
1506			if (addr6.preferlifetime > addr6.validlifetime) {
1507				dprintf(LOG_INFO, "preferred life time"
1508				    "(%d) is greater than valid life time"
1509				    "(%d)", addr6.preferlifetime, addr6.validlifetime);
1510				goto malformed;
1511			}
1512			if (optlen == sizeof(ai) - sizeof(u_int32_t)) {
1513				addr6.status_code = DH6OPT_STCODE_UNDEFINE;
1514				break;
1515			}
1516			/* address status code might be added after IADDA option */
1517			memcpy(&opth, p + sizeof(ai), sizeof(opth));
1518			optlen =  ntohs(opth.dh6opt_len);
1519			opt = ntohs(opth.dh6opt_type);
1520			switch(opt) {
1521			case DH6OPT_STATUS_CODE:
1522				if (optlen < sizeof(val16))
1523					goto malformed;
1524				memcpy(&val16, p + sizeof(ai) + sizeof(opth), sizeof(val16));
1525				num = ntohs(val16);
1526				dprintf(LOG_INFO, "status code for this address is: %s",
1527					dhcp6_stcodestr(num));
1528				addr6.status_code = num;
1529				if (optlen > sizeof(val16)) {
1530					dprintf(LOG_INFO,
1531						"status message for this address is: %-*s",
1532						(int)(optlen-sizeof(val16)), p+(val16));
1533				}
1534				break;
1535			default:
1536				goto malformed;
1537			}
1538			break;
1539		case DH6OPT_IAPREFIX:
1540			if (optlen < sizeof(pi) - sizeof(u_int32_t))
1541			       goto malformed;
1542			memcpy(&pi, p, sizeof(pi));
1543			/* copy the information into internal format */
1544			memset(&addr6, 0, sizeof(addr6));
1545			addr6.preferlifetime = ntohl(pi.preferlifetime);
1546			addr6.validlifetime = ntohl(pi.validlifetime);
1547			addr6.plen = pi.plen;
1548			memcpy(&addr6.addr, &pi.prefix, sizeof(struct in6_addr));
1549			dprintf(LOG_DEBUG, "  get IAPREFIX prefix information: "
1550			    "%s/%d preferlifetime %d validlifetime %d",
1551			    in6addr2str(&addr6.addr, 0), addr6.plen,
1552			    addr6.preferlifetime, addr6.validlifetime);
1553			/* It shouldn't happen, since Server will do the check before
1554			 * sending the data to clients */
1555			if (addr6.preferlifetime > addr6.validlifetime) {
1556				dprintf(LOG_INFO, "preferred life time"
1557				    "(%d) is greater than valid life time"
1558				    "(%d)", addr6.preferlifetime, addr6.validlifetime);
1559				goto malformed;
1560			}
1561
1562			/* Foxconn added start pling 12/22/2011 */
1563			/* WNDR4500 TD#156: Record the IAPD valid and preferred lifetime */
1564			if (dhcp6_mode == DHCP6_MODE_CLIENT) {
1565				sprintf(iapd_valid_lifetime_cmd_buf,
1566						"nvram set RA_AdvValidLifetime_from_IAPD=%u",
1567						addr6.validlifetime);
1568				sprintf(iapd_preferred_lifetime_cmd_buf,
1569						"nvram set RA_AdvPreferredLifetime_from_IAPD=%u",
1570						addr6.preferlifetime);
1571			}
1572			/* Foxconn added end pling 12/22/2011 */
1573
1574            if (!(dhcp6c_flags & DHCIFF_IAPD_ONLY))
1575    			addr6.type = IAPD;      /* Foxconn added pling 01/25/2009 */
1576			if (optlen == sizeof(pi) - sizeof(u_int32_t)) {
1577				addr6.status_code = DH6OPT_STCODE_UNDEFINE;
1578				break;
1579			}
1580			/* address status code might be added after IADDA option */
1581			memcpy(&opth, p + sizeof(pi), sizeof(opth));
1582			optlen =  ntohs(opth.dh6opt_len);
1583			opt = ntohs(opth.dh6opt_type);
1584			switch(opt) {
1585			case DH6OPT_STATUS_CODE:
1586				if (optlen < sizeof(val16))
1587					goto malformed;
1588				memcpy(&val16, p + sizeof(pi) + sizeof(opth), sizeof(val16));
1589				num = ntohs(val16);
1590				dprintf(LOG_INFO, "status code for this prefix is: %s",
1591					dhcp6_stcodestr(num));
1592				addr6.status_code = num;
1593				if (optlen > sizeof(val16)) {
1594					dprintf(LOG_INFO,
1595						"status message for this prefix is: %-*s",
1596						(int)(optlen-sizeof(val16)), p+(val16));
1597				}
1598				break;
1599			default:
1600				goto malformed;
1601			}
1602			break;
1603		default:
1604			goto malformed;
1605		}
1606
1607no_status_code: /* Foxconn added start pling 09/15/2011 */
1608		/* set up address type */
1609		/* Foxconn added start pling 09/23/2009 */
1610		if (addr6.type == IAPD) {
1611		    /* Foxconn added start pling 01/25/2010 */
1612    		if (dhcp6_find_listval(&optinfo->prefix_list,
1613	    			&addr6, DHCP6_LISTVAL_DHCP6ADDR)) {
1614		    	dprintf(LOG_INFO, "duplicated prefix (%s/%d)",
1615			    	in6addr2str(&addr6.addr, 0), addr6.plen);
1616    			continue;
1617	    	}
1618		    /* Foxconn added end pling 01/25/2010 */
1619			if (dhcp6_add_listval(&optinfo->prefix_list, &addr6,
1620			    DHCP6_LISTVAL_DHCP6ADDR) == NULL) {
1621				dprintf(LOG_ERR, "%s" "failed to copy prefix", FNAME);
1622				goto fail;
1623			}
1624		} else {
1625		/* Foxconn added end pling 09/23/2009 */
1626		addr6.type = optinfo->type;
1627		if (dhcp6_find_listval(&optinfo->addr_list,
1628				&addr6, DHCP6_LISTVAL_DHCP6ADDR)) {
1629			dprintf(LOG_INFO, "duplicated address (%s/%d)",
1630				in6addr2str(&addr6.addr, 0), addr6.plen);
1631			continue;
1632		}
1633		if (dhcp6_add_listval(&optinfo->addr_list, &addr6,
1634		    DHCP6_LISTVAL_DHCP6ADDR) == NULL) {
1635			dprintf(LOG_ERR, "%s" "failed to copy an "
1636			    "address", FNAME);
1637			goto fail;
1638		}
1639		/* Foxconn added start pling 09/23/2009 */
1640		} /* if (addr6.type == IAPD) */
1641		/* Foxconn added end pling 09/23/2009 */
1642	}
1643
1644	/* Foxconn added start pling 12/22/2011 */
1645	/* WNDR4500 TD#156: Set the IAPD valid and preferred lifetime
1646	 * to NVRAM for acos rc to use */
1647	if (dhcp6_mode == DHCP6_MODE_CLIENT) {
1648		system(iapd_valid_lifetime_cmd_buf);
1649		system(iapd_preferred_lifetime_cmd_buf);
1650		system("nvram set RA_use_dynamic_lifetime=1");
1651	}
1652	/* Foxconn added end pling 12/22/2011 */
1653
1654	return (0);
1655
1656  malformed:
1657	dprintf(LOG_INFO,
1658		"  malformed IA option: type %d, len %d",
1659		opt, optlen);
1660  fail:
1661	return (-1);
1662}
1663
1664#define COPY_OPTION(t, l, v, p) do { \
1665	if ((void *)(ep) - (void *)(p) < (l) + sizeof(struct dhcp6opt)) { \
1666		dprintf(LOG_INFO, "%s" "option buffer short for %s", FNAME, dhcp6optstr((t))); \
1667		goto fail; \
1668	} \
1669	opth.dh6opt_type = htons((t)); \
1670	opth.dh6opt_len = htons((l)); \
1671	memcpy((p), &opth, sizeof(opth)); \
1672	if ((l)) \
1673		memcpy((p) + 1, (v), (l)); \
1674	(p) = (struct dhcp6opt *)((char *)((p) + 1) + (l)); \
1675 	(len) += sizeof(struct dhcp6opt) + (l); \
1676	dprintf(LOG_DEBUG, "%s" "set %s", FNAME, dhcp6optstr((t))); \
1677} while (0)
1678
1679int
1680dhcp6_set_options(bp, ep, optinfo)
1681	struct dhcp6opt *bp, *ep;
1682	struct dhcp6_optinfo *optinfo;
1683{
1684	struct dhcp6opt *p = bp, opth;
1685	struct dhcp6_listval *stcode;
1686	int len = 0, optlen = 0;
1687	char *tmpbuf = NULL;
1688
1689	if (optinfo->clientID.duid_len) {
1690		COPY_OPTION(DH6OPT_CLIENTID, optinfo->clientID.duid_len,
1691			    optinfo->clientID.duid_id, p);
1692	}
1693
1694	if (optinfo->serverID.duid_len) {
1695		COPY_OPTION(DH6OPT_SERVERID, optinfo->serverID.duid_len,
1696			    optinfo->serverID.duid_id, p);
1697	}
1698	if (dhcp6_mode == DHCP6_MODE_CLIENT)
1699    {
1700        /* Foxconn modified start pling 10/01/2010 */
1701        /* Take care of endian issue */
1702		// COPY_OPTION(DH6OPT_ELAPSED_TIME, 2, &optinfo->elapsed_time, p);
1703        u_int16_t elapsed_time = htons(optinfo->elapsed_time);
1704        COPY_OPTION(DH6OPT_ELAPSED_TIME, 2, &elapsed_time, p);
1705        /* Foxconn modified end pling 10/01/2010 */
1706    }
1707
1708    /* Foxconn added start pling 09/07/2010 */
1709    /* For dhcp6c, add user-class if specified */
1710    if (dhcp6_mode == DHCP6_MODE_CLIENT)
1711    {
1712        /* user class option in this format (RFC3315):
1713        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1714        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1715        | OPTION_USER_CLASS               | option-len                  |
1716        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1717        | user-class-data                                               .
1718        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1719
1720         user-class-data in this format:
1721        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
1722        | user-class-len (2 bytes)        | opaque-data                 |
1723        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
1724         */
1725        int option_len;
1726        unsigned short *user_class_len;
1727        char user_class_data[MAX_USER_CLASS_LEN+2];
1728
1729        if (strlen(optinfo->user_class))
1730        {
1731            option_len = strlen(optinfo->user_class) + 2;
1732            user_class_len = (unsigned short *)&user_class_data;
1733            *user_class_len = htons(strlen(optinfo->user_class));
1734            strcpy(&user_class_data[2], optinfo->user_class);
1735            COPY_OPTION(DH6OPT_USER_CLASS, option_len, user_class_data, p);
1736        }
1737    }
1738    /* Foxconn added end pling 09/07/2010 */
1739
1740	if (optinfo->flags & DHCIFF_RAPID_COMMIT)
1741		COPY_OPTION(DH6OPT_RAPID_COMMIT, 0, NULL, p);
1742
1743	if ((dhcp6_mode == DHCP6_MODE_SERVER) && (optinfo->flags & DHCIFF_UNICAST)) {
1744		if (!IN6_IS_ADDR_UNSPECIFIED(&optinfo->server_addr)) {
1745			COPY_OPTION(DH6OPT_UNICAST, sizeof(optinfo->server_addr),
1746				    &optinfo->server_addr, p);
1747		}
1748	}
1749	switch(optinfo->type) {
1750	int buflen;
1751	char *tp;
1752	u_int32_t iaid;
1753	struct dhcp6_iaid_info opt_iana;
1754	struct dhcp6_iaid_info opt_iapd;
1755	struct dhcp6_prefix_info pi;
1756	struct dhcp6_addr_info ai;
1757	struct dhcp6_status_info status;
1758	struct dhcp6_listval *dp;
1759	case IATA:
1760	case IANA:
1761		if (optinfo->iaidinfo.iaid == 0)
1762			break;
1763		if (optinfo->type == IATA) {
1764			optlen = sizeof(iaid);
1765			dprintf(LOG_DEBUG, "%s" "set IA_TA iaid information: %d", FNAME,
1766				optinfo->iaidinfo.iaid);
1767			iaid = htonl(optinfo->iaidinfo.iaid);
1768		} else if (optinfo->type == IANA) {
1769			optlen = sizeof(opt_iana);
1770			dprintf(LOG_DEBUG, "set IA_NA iaidinfo: "
1771		   		"iaid %u renewtime %u rebindtime %u",
1772		   		optinfo->iaidinfo.iaid, optinfo->iaidinfo.renewtime,
1773		   		optinfo->iaidinfo.rebindtime);
1774			opt_iana.iaid = htonl(optinfo->iaidinfo.iaid);
1775		    /* Foxconn modified start pling 01/25/2010 */
1776    		/* Per Netgear spec, use IAID '11' for IAPD in dhcp6c */
1777	    	if (dhcp6_mode == DHCP6_MODE_CLIENT)
1778		    	opt_iana.iaid = htonl(IANA_IAID);
1779    		/* Foxconn modified end pling 01/25/2010 */
1780			opt_iana.renewtime = htonl(optinfo->iaidinfo.renewtime);
1781			opt_iana.rebindtime = htonl(optinfo->iaidinfo.rebindtime);
1782		}
1783        /* Foxconn modified start pling 09/24/2009 */
1784		if (dhcp6_mode == DHCP6_MODE_SERVER ||
1785            dhcp6_mode == DHCP6_MODE_CLIENT && dhcp6c_flags & DHCIFF_IAPD_ONLY) {
1786		    buflen = sizeof(opt_iana) + dhcp6_count_list(&optinfo->addr_list) *
1787			    	(sizeof(ai) + sizeof(status));
1788        } else {
1789            /* Client don't need to send the status code */
1790		    buflen = sizeof(opt_iana) + dhcp6_count_list(&optinfo->addr_list) *
1791			    	 sizeof(ai);
1792        }
1793        /* Foxconn modified end pling 09/24/2009 */
1794		tmpbuf = NULL;
1795		if ((tmpbuf = malloc(buflen)) == NULL) {
1796			dprintf(LOG_ERR, "%s"
1797				"memory allocation failed for options", FNAME);
1798			goto fail;
1799		}
1800		if (optinfo->type == IATA)
1801			memcpy(tmpbuf, &iaid, sizeof(iaid));
1802		else
1803			memcpy(tmpbuf, &opt_iana, sizeof(opt_iana));
1804		tp = tmpbuf + optlen;
1805		optlen += dhcp6_count_list(&optinfo->addr_list) * sizeof(ai);
1806		if (!TAILQ_EMPTY(&optinfo->addr_list)) {
1807			for (dp = TAILQ_FIRST(&optinfo->addr_list); dp;
1808			     dp = TAILQ_NEXT(dp, link)) {
1809				int iaddr_len = 0;
1810				memset(&ai, 0, sizeof(ai));
1811				ai.dh6_ai_type = htons(DH6OPT_IADDR);
1812				if (dp->val_dhcp6addr.status_code != DH6OPT_STCODE_UNDEFINE) {
1813                    /* Foxconn modified start pling 09/24/2009 */
1814		            if (dhcp6_mode == DHCP6_MODE_SERVER)
1815					    iaddr_len = sizeof(ai) - sizeof(u_int32_t)
1816						    		+ sizeof(status);
1817                    else
1818					    iaddr_len = sizeof(ai) - sizeof(u_int32_t);
1819                } else
1820					iaddr_len = sizeof(ai) - sizeof(u_int32_t);
1821				ai.dh6_ai_len = htons(iaddr_len);
1822				ai.preferlifetime = htonl(dp->val_dhcp6addr.preferlifetime);
1823				ai.validlifetime = htonl(dp->val_dhcp6addr.validlifetime);
1824				memcpy(&ai.addr, &dp->val_dhcp6addr.addr,
1825			       		sizeof(ai.addr));
1826				memcpy(tp, &ai, sizeof(ai));
1827				tp += sizeof(ai);
1828				dprintf(LOG_DEBUG, "set IADDR address option len %d: "
1829			    		"%s preferlifetime %d validlifetime %d",
1830			    		iaddr_len, in6addr2str(&ai.addr, 0),
1831			    		ntohl(ai.preferlifetime),
1832					ntohl(ai.validlifetime));
1833				/* set up address status code if any */
1834                /* Foxconn added start pling 09/24/2009 */
1835                /* Don't add status code in client reqeust */
1836		        if (dhcp6_mode == DHCP6_MODE_CLIENT)
1837                    ;
1838                else
1839                /* Foxconn added end pling 09/24/2009 */
1840				if (dp->val_dhcp6addr.status_code != DH6OPT_STCODE_UNDEFINE) {
1841					status.dh6_status_type = htons(DH6OPT_STATUS_CODE);
1842					status.dh6_status_len =
1843						htons(sizeof(status.dh6_status_code));
1844					status.dh6_status_code =
1845						htons(dp->val_dhcp6addr.status_code);
1846					memcpy(tp, &status, sizeof(status));
1847					dprintf(LOG_DEBUG, "  this address status code: %s",
1848			    		dhcp6_stcodestr(ntohs(status.dh6_status_code)));
1849					optlen += sizeof(status);
1850					tp += sizeof(status);
1851                }
1852			}
1853		} else if (dhcp6_mode == DHCP6_MODE_SERVER) {
1854			int num;
1855			num = DH6OPT_STCODE_NOADDRAVAIL;
1856			dprintf(LOG_DEBUG, "  status code: %s",
1857			    dhcp6_stcodestr(num));
1858			if (dhcp6_add_listval(&optinfo->stcode_list,
1859			    &num, DHCP6_LISTVAL_NUM) == NULL) {
1860				dprintf(LOG_ERR, "%s" "failed to copy "
1861				    "status code", FNAME);
1862				goto fail;
1863			}
1864		}
1865		if (optinfo->type == IATA)
1866			COPY_OPTION(DH6OPT_IA_TA, optlen, tmpbuf, p);
1867		else if (optinfo->type == IANA)
1868			COPY_OPTION(DH6OPT_IA_NA, optlen, tmpbuf, p);
1869		free(tmpbuf);
1870		/* Foxconn modified start pling 09/22/2009 */
1871		/* Per Netgear spec, dhcp6c need to send IAPD,
1872		 *  so we fall through to do IAPD.
1873		 */
1874		if (dhcp6_mode == DHCP6_MODE_SERVER)
1875			break;
1876		/* Foxconn modified end pling 09/22/2009 */
1877        /* Foxconn added start pling 10/01/2010 */
1878        /* For DHCPv6 readylogo test, send IANA only */
1879        if (dhcp6_mode == DHCP6_MODE_CLIENT &&
1880            dhcp6c_flags & DHCIFF_IANA_ONLY)
1881            break;
1882        /* Foxconn added end pling 10/01/2010 */
1883	case IAPD:
1884		/* Foxconn modified start pling 09/22/2009 */
1885		/* Per Netgear spec, use IAID '11' for IAPD in dhcp6c */
1886		if (dhcp6_mode == DHCP6_MODE_CLIENT)
1887			optinfo->iaidinfo.iaid = IAPD_IAID;
1888		/* Foxconn modified end pling 09/22/2009 */
1889		if (optinfo->iaidinfo.iaid == 0)
1890			break;
1891		optlen = sizeof(opt_iapd);
1892		dprintf(LOG_DEBUG, "set IA_PD iaidinfo: "
1893		 	"iaid %u renewtime %u rebindtime %u",
1894		  	optinfo->iaidinfo.iaid, optinfo->iaidinfo.renewtime,
1895		   	optinfo->iaidinfo.rebindtime);
1896		opt_iapd.iaid = htonl(optinfo->iaidinfo.iaid);
1897		opt_iapd.renewtime = htonl(optinfo->iaidinfo.renewtime);
1898		opt_iapd.rebindtime = htonl(optinfo->iaidinfo.rebindtime);
1899		/* Foxconn modified start pling 09/23/2009 */
1900        /* In DHCP client mode, copy the prefix,
1901         * but not include the status code
1902         */
1903		if (dhcp6_mode == DHCP6_MODE_SERVER ||
1904            dhcp6_mode == DHCP6_MODE_CLIENT && dhcp6c_flags & DHCIFF_IAPD_ONLY)
1905		    buflen = sizeof(opt_iapd) + dhcp6_count_list(&optinfo->addr_list) *
1906			    	(sizeof(pi) + sizeof(status));
1907        else
1908    		buflen = sizeof(opt_iapd) + dhcp6_count_list(&optinfo->prefix_list) *
1909 	    			sizeof(pi);
1910		/* Foxconn modified end pling 09/23/2009 */
1911		tmpbuf = NULL;
1912		if ((tmpbuf = malloc(buflen)) == NULL) {
1913			dprintf(LOG_ERR, "%s"
1914				"memory allocation failed for options", FNAME);
1915			goto fail;
1916		}
1917		memcpy(tmpbuf, &opt_iapd, sizeof(opt_iapd));
1918		tp = tmpbuf + optlen;
1919		/* Foxconn modified start pling 09/23/2009 */
1920        /* IAPD is handle differently in server and client mode */
1921		if (dhcp6_mode == DHCP6_MODE_SERVER ||
1922            dhcp6_mode == DHCP6_MODE_CLIENT && dhcp6c_flags & DHCIFF_IAPD_ONLY) {
1923		    optlen += dhcp6_count_list(&optinfo->addr_list) * sizeof(pi);
1924    		if (!TAILQ_EMPTY(&optinfo->addr_list)) {
1925	    		for (dp = TAILQ_FIRST(&optinfo->addr_list); dp;
1926			        dp = TAILQ_NEXT(dp, link)) {
1927    				int iaddr_len = 0;
1928	    			memset(&pi, 0, sizeof(pi));
1929		    		pi.dh6_pi_type = htons(DH6OPT_IAPREFIX);
1930			    	if (dp->val_dhcp6addr.status_code != DH6OPT_STCODE_UNDEFINE)
1931    					iaddr_len = sizeof(pi) - sizeof(u_int32_t)
1932	    					+ sizeof(status);
1933				    else
1934					    iaddr_len = sizeof(pi) - sizeof(u_int32_t);
1935    				pi.dh6_pi_len = htons(iaddr_len);
1936	    			pi.preferlifetime = htonl(dp->val_dhcp6addr.preferlifetime);
1937		    		pi.validlifetime = htonl(dp->val_dhcp6addr.validlifetime);
1938			    	pi.plen = dp->val_dhcp6addr.plen;
1939				    memcpy(&pi.prefix, &dp->val_dhcp6addr.addr, sizeof(pi.prefix));
1940    				memcpy(tp, &pi, sizeof(pi));
1941	    			tp += sizeof(pi);
1942		    		dprintf(LOG_DEBUG, "set IAPREFIX option len %d: "
1943			        		"%s/%d preferlifetime %d validlifetime %d",
1944			        		iaddr_len, in6addr2str(&pi.prefix, 0), pi.plen,
1945			    	    	ntohl(pi.preferlifetime), ntohl(pi.validlifetime));
1946    				/* set up address status code if any */
1947    				if (dp->val_dhcp6addr.status_code != DH6OPT_STCODE_UNDEFINE) {
1948	    				status.dh6_status_type = htons(DH6OPT_STATUS_CODE);
1949		    			status.dh6_status_len =
1950			    			htons(sizeof(status.dh6_status_code));
1951				    	status.dh6_status_code =
1952					    	htons(dp->val_dhcp6addr.status_code);
1953    					memcpy(tp, &status, sizeof(status));
1954	    				dprintf(LOG_DEBUG, "  this address status code: %s",
1955		    	    		dhcp6_stcodestr(ntohs(status.dh6_status_code)));
1956			    		optlen += sizeof(status);
1957				    	tp += sizeof(status);
1958					    /* copy status message if any */
1959    				}
1960                }
1961		    } else if (dhcp6_mode == DHCP6_MODE_SERVER) {
1962			    int num;
1963    			num = DH6OPT_STCODE_NOPREFIXAVAIL;
1964	    		dprintf(LOG_DEBUG, "  status code: %s",
1965		    	    dhcp6_stcodestr(num));
1966			    if (dhcp6_add_listval(&optinfo->stcode_list,
1967			        &num, DHCP6_LISTVAL_NUM) == NULL) {
1968    				dprintf(LOG_ERR, "%s" "failed to copy "
1969	    			    "status code", FNAME);
1970		    		goto fail;
1971			    }
1972    		}
1973        } else {
1974            /* Client mode */
1975            /* Use 'prefix_list' instead of 'addr_list' for IAPD */
1976    		optlen += dhcp6_count_list(&optinfo->prefix_list) * sizeof(pi);
1977	    	if (!TAILQ_EMPTY(&optinfo->prefix_list)) {
1978		    	for (dp = TAILQ_FIRST(&optinfo->prefix_list); dp;
1979			         dp = TAILQ_NEXT(dp, link)) {
1980				    int iaddr_len = 0;
1981    				memset(&pi, 0, sizeof(pi));
1982	    			pi.dh6_pi_type = htons(DH6OPT_IAPREFIX);
1983		    		if (dp->val_dhcp6addr.status_code != DH6OPT_STCODE_UNDEFINE)
1984					    iaddr_len = sizeof(pi) - sizeof(u_int32_t);
1985    				else
1986	    				iaddr_len = sizeof(pi) - sizeof(u_int32_t);
1987		    		pi.dh6_pi_len = htons(iaddr_len);
1988			    	pi.preferlifetime = htonl(dp->val_dhcp6addr.preferlifetime);
1989				    pi.validlifetime = htonl(dp->val_dhcp6addr.validlifetime);
1990    				pi.plen = dp->val_dhcp6addr.plen;
1991	    			memcpy(&pi.prefix, &dp->val_dhcp6addr.addr, sizeof(pi.prefix));
1992		    		memcpy(tp, &pi, sizeof(pi));
1993			    	tp += sizeof(pi);
1994				    dprintf(LOG_DEBUG, "set IAPREFIX option len %d: "
1995			    	    	"%s/%d preferlifetime %d validlifetime %d",
1996			    		    iaddr_len, in6addr2str(&pi.prefix, 0), pi.plen,
1997    			    		ntohl(pi.preferlifetime), ntohl(pi.validlifetime));
1998                }
1999			}
2000		}
2001        /* Foxconn modified end 09/23/2009 */
2002		COPY_OPTION(DH6OPT_IA_PD, optlen, tmpbuf, p);
2003		free(tmpbuf);
2004		break;
2005	default:
2006		break;
2007	}
2008	if (dhcp6_mode == DHCP6_MODE_SERVER && optinfo->pref != DH6OPT_PREF_UNDEF) {
2009		u_int8_t p8 = (u_int8_t)optinfo->pref;
2010		dprintf(LOG_DEBUG, "server preference %2x", optinfo->pref);
2011		COPY_OPTION(DH6OPT_PREFERENCE, sizeof(p8), &p8, p);
2012	}
2013
2014	for (stcode = TAILQ_FIRST(&optinfo->stcode_list); stcode;
2015	     stcode = TAILQ_NEXT(stcode, link)) {
2016		u_int16_t code;
2017
2018		code = htons(stcode->val_num);
2019		COPY_OPTION(DH6OPT_STATUS_CODE, sizeof(code), &code, p);
2020	}
2021
2022	if (!TAILQ_EMPTY(&optinfo->reqopt_list)) {
2023		struct dhcp6_listval *opt;
2024		u_int16_t *valp;
2025
2026		tmpbuf = NULL;
2027		optlen = dhcp6_count_list(&optinfo->reqopt_list) *
2028			sizeof(u_int16_t);
2029		if ((tmpbuf = malloc(optlen)) == NULL) {
2030			dprintf(LOG_ERR, "%s"
2031			    "memory allocation failed for options", FNAME);
2032			goto fail;
2033		}
2034		valp = (u_int16_t *)tmpbuf;
2035		for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt;
2036		     opt = TAILQ_NEXT(opt, link), valp++) {
2037			*valp = htons((u_int16_t)opt->val_num);
2038		}
2039		COPY_OPTION(DH6OPT_ORO, optlen, tmpbuf, p);
2040		free(tmpbuf);
2041	}
2042
2043	if (!TAILQ_EMPTY(&optinfo->dns_list.addrlist)) {
2044		struct in6_addr *in6;
2045		struct dhcp6_listval *d;
2046
2047		tmpbuf = NULL;
2048		optlen = dhcp6_count_list(&optinfo->dns_list.addrlist) *
2049			sizeof(struct in6_addr);
2050		if ((tmpbuf = malloc(optlen)) == NULL) {
2051			dprintf(LOG_ERR, "%s"
2052			    "memory allocation failed for DNS options", FNAME);
2053			goto fail;
2054		}
2055		in6 = (struct in6_addr *)tmpbuf;
2056		for (d = TAILQ_FIRST(&optinfo->dns_list.addrlist); d;
2057		     d = TAILQ_NEXT(d, link), in6++) {
2058			memcpy(in6, &d->val_addr6, sizeof(*in6));
2059		}
2060		COPY_OPTION(DH6OPT_DNS_SERVERS, optlen, tmpbuf, p);
2061		free(tmpbuf);
2062	}
2063
2064    /* Foxconn added start pling 01/25/2010 */
2065	if (!TAILQ_EMPTY(&optinfo->sip_list)) {
2066		struct in6_addr *in6;
2067		struct dhcp6_listval *d;
2068
2069		tmpbuf = NULL;
2070		optlen = dhcp6_count_list(&optinfo->sip_list) *
2071			sizeof(struct in6_addr);
2072		if ((tmpbuf = malloc(optlen)) == NULL) {
2073			dprintf(LOG_ERR, "%s"
2074			    "memory allocation failed for SIP options", FNAME);
2075			goto fail;
2076		}
2077		in6 = (struct in6_addr *)tmpbuf;
2078		for (d = TAILQ_FIRST(&optinfo->sip_list); d;
2079		     d = TAILQ_NEXT(d, link), in6++) {
2080			memcpy(in6, &d->val_addr6, sizeof(*in6));
2081		}
2082		COPY_OPTION(DH6OPT_SIP_SERVERS, optlen, tmpbuf, p);
2083		free(tmpbuf);
2084	}
2085	if (!TAILQ_EMPTY(&optinfo->ntp_list)) {
2086		struct in6_addr *in6;
2087		struct dhcp6_listval *d;
2088
2089		tmpbuf = NULL;
2090		optlen = dhcp6_count_list(&optinfo->ntp_list) *
2091			sizeof(struct in6_addr);
2092		if ((tmpbuf = malloc(optlen)) == NULL) {
2093			dprintf(LOG_ERR, "%s"
2094			    "memory allocation failed for NTP options", FNAME);
2095			goto fail;
2096		}
2097		in6 = (struct in6_addr *)tmpbuf;
2098		for (d = TAILQ_FIRST(&optinfo->ntp_list); d;
2099		     d = TAILQ_NEXT(d, link), in6++) {
2100			memcpy(in6, &d->val_addr6, sizeof(*in6));
2101		}
2102		COPY_OPTION(DH6OPT_NTP_SERVERS, optlen, tmpbuf, p);
2103		free(tmpbuf);
2104	}
2105    /* Foxconn added end pling 01/25/2010 */
2106
2107	if (optinfo->dns_list.domainlist != NULL) {
2108		struct domain_list *dlist;
2109		u_char *dst;
2110		optlen = 0;
2111		tmpbuf = NULL;
2112		if ((tmpbuf = malloc(MAXDNAME * MAXDN)) == NULL) {
2113			dprintf(LOG_ERR, "%s"
2114			    "memory allocation failed for DNS options", FNAME);
2115			goto fail;
2116		}
2117		dst = tmpbuf;
2118		for (dlist = optinfo->dns_list.domainlist; dlist; dlist = dlist->next) {
2119			int n;
2120			n = dn_comp(dlist->name, dst, MAXDNAME, NULL, NULL);
2121			if (n < 0) {
2122				dprintf(LOG_ERR, "%s" "compress domain name failed", FNAME);
2123				goto fail;
2124			} else
2125				dprintf(LOG_DEBUG, "compress domain name %s", dlist->name);
2126			optlen += n ;
2127			dst += n;
2128		}
2129		COPY_OPTION(DH6OPT_DOMAIN_LIST, optlen, tmpbuf, p);
2130		free(tmpbuf);
2131	}
2132
2133
2134	return (len);
2135
2136  fail:
2137	if (tmpbuf)
2138		free(tmpbuf);
2139	return (-1);
2140}
2141#undef COPY_OPTION
2142
2143void
2144dhcp6_set_timeoparam(ev)
2145	struct dhcp6_event *ev;
2146{
2147	ev->retrans = 0;
2148	ev->init_retrans = 0;
2149	ev->max_retrans_cnt = 0;
2150	ev->max_retrans_dur = 0;
2151	ev->max_retrans_time = 0;
2152
2153	switch(ev->state) {
2154	case DHCP6S_SOLICIT:
2155		ev->init_retrans = SOL_TIMEOUT;
2156		ev->max_retrans_time = SOL_MAX_RT;
2157		break;
2158	case DHCP6S_INFOREQ:
2159		ev->init_retrans = INF_TIMEOUT;
2160		ev->max_retrans_time = INF_MAX_RT;
2161		break;
2162	case DHCP6S_REQUEST:
2163		ev->init_retrans = REQ_TIMEOUT;
2164		ev->max_retrans_time = REQ_MAX_RT;
2165		ev->max_retrans_cnt = REQ_MAX_RC;
2166		break;
2167	case DHCP6S_RENEW:
2168		ev->init_retrans = REN_TIMEOUT;
2169		ev->max_retrans_time = REN_MAX_RT;
2170		break;
2171	case DHCP6S_REBIND:
2172		ev->init_retrans = REB_TIMEOUT;
2173		ev->max_retrans_time = REB_MAX_RT;
2174		break;
2175        case DHCP6S_DECLINE:
2176                ev->init_retrans = DEC_TIMEOUT;
2177                ev->max_retrans_cnt = DEC_MAX_RC;
2178                break;
2179        case DHCP6S_RELEASE:
2180                ev->init_retrans = REL_TIMEOUT;
2181                ev->max_retrans_cnt = REL_MAX_RC;
2182                break;
2183        case DHCP6S_CONFIRM:
2184                ev->init_retrans = CNF_TIMEOUT;
2185                ev->max_retrans_dur = CNF_MAX_RD;
2186                ev->max_retrans_time = CNF_MAX_RT;
2187		break;
2188	default:
2189		dprintf(LOG_INFO, "%s" "unexpected event state %d on %s",
2190		    FNAME, ev->state, ev->ifp->ifname);
2191		exit(1);
2192	}
2193}
2194
2195void
2196dhcp6_reset_timer(ev)
2197	struct dhcp6_event *ev;
2198{
2199	double n, r;
2200	char *statestr;
2201	struct timeval interval;
2202
2203	switch(ev->state) {
2204	case DHCP6S_INIT:
2205		/*
2206		 * The first Solicit message from the client on the interface
2207		 * MUST be delayed by a random amount of time between
2208		 * MIN_SOL_DELAY and MAX_SOL_DELAY.
2209		 * [dhcpv6-28 14.]
2210		 */
2211        /* Foxconn modified start pling 08/26/2009 */
2212        /* In IPv6 auto mode (when DHCIFF_SOLICIT_ONLY is set),
2213         * send immediately.
2214         */
2215        if ((ev->ifp->send_flags & DHCIFF_SOLICIT_ONLY))
2216            ev->retrans = 0;
2217        else
2218		    ev->retrans = (random() % (MAX_SOL_DELAY - MIN_SOL_DELAY)) +
2219			    MIN_SOL_DELAY;
2220        /* Foxconn modified end pling 08/26/2009 */
2221		break;
2222	default:
2223		if (ev->timeouts == 0) {
2224			/*
2225			 * The first RT MUST be selected to be strictly
2226			 * greater than IRT by choosing RAND to be strictly
2227			 * greater than 0.
2228			 * [dhcpv6-28 14.]
2229			 */
2230			r = (double)((random() % 1000) + 1) / 10000;
2231			n = ev->init_retrans + r * ev->init_retrans;
2232		} else {
2233			r = (double)((random() % 2000) - 1000) / 10000;
2234
2235			if (ev->timeouts == 0) {
2236				n = ev->init_retrans + r * ev->init_retrans;
2237			} else
2238				n = 2 * ev->retrans + r * ev->retrans;
2239		}
2240		if (ev->max_retrans_time && n > ev->max_retrans_time)
2241			n = ev->max_retrans_time + r * ev->max_retrans_time;
2242        /* Foxconn modified start pling 08/26/2009 */
2243        /* In IPv6 auto mode (when DHCIFF_SOLICIT_ONLY is set),
2244         * then send 1 DHCP Solicit every 1 sec.
2245         */
2246        if ((ev->ifp->send_flags & DHCIFF_SOLICIT_ONLY))
2247            ev->retrans = 1000;
2248        else
2249    		ev->retrans = (long)n;
2250        /* Foxconn modified end pling 08/26/2009 */
2251		break;
2252	}
2253
2254	switch(ev->state) {
2255	case DHCP6S_INIT:
2256		statestr = "INIT";
2257		break;
2258	case DHCP6S_SOLICIT:
2259		statestr = "SOLICIT";
2260		break;
2261	case DHCP6S_INFOREQ:
2262		statestr = "INFOREQ";
2263		break;
2264	case DHCP6S_REQUEST:
2265		statestr = "REQUEST";
2266		break;
2267	case DHCP6S_RENEW:
2268		statestr = "RENEW";
2269		break;
2270	case DHCP6S_REBIND:
2271		statestr = "REBIND";
2272		break;
2273	case DHCP6S_CONFIRM:
2274		statestr = "CONFIRM";
2275		break;
2276	case DHCP6S_DECLINE:
2277		statestr = "DECLINE";
2278		break;
2279	case DHCP6S_RELEASE:
2280		statestr = "RELEASE";
2281		break;
2282	case DHCP6S_IDLE:
2283		statestr = "IDLE";
2284		break;
2285	default:
2286		statestr = "???";
2287		break;
2288	}
2289
2290	interval.tv_sec = (ev->retrans * 1000) / 1000000;
2291	interval.tv_usec = (ev->retrans * 1000) % 1000000;
2292	dhcp6_set_timer(&interval, ev->timer);
2293
2294	dprintf(LOG_DEBUG, "%s" "reset a timer on %s, "
2295		"state=%s, timeo=%d, retrans=%ld", FNAME,
2296		ev->ifp->ifname, statestr, ev->timeouts, (long) ev->retrans);
2297}
2298
2299int
2300duidcpy(struct duid *dd, const struct duid *ds)
2301{
2302	dd->duid_len = ds->duid_len;
2303	if ((dd->duid_id = malloc(dd->duid_len)) == NULL) {
2304		dprintf(LOG_ERR, "%s" "len %d memory allocation failed", FNAME, dd->duid_len);
2305		return (-1);
2306	}
2307	memcpy(dd->duid_id, ds->duid_id, dd->duid_len);
2308
2309	return (0);
2310}
2311
2312int
2313duidcmp(const struct duid *d1,
2314	const struct duid *d2)
2315{
2316	if (d1->duid_len == d2->duid_len) {
2317		return (memcmp(d1->duid_id, d2->duid_id, d1->duid_len));
2318	} else
2319		return (-1);
2320}
2321
2322void
2323duidfree(duid)
2324	struct duid *duid;
2325{
2326	dprintf(LOG_DEBUG, "%s" "DUID is %s, DUID_LEN is %d",
2327			FNAME, duidstr(duid), duid->duid_len);
2328	if (duid->duid_id != NULL && duid->duid_len != 0) {
2329		dprintf(LOG_DEBUG, "%s" "removing ID (ID: %s)",
2330		    FNAME, duidstr(duid));
2331		free(duid->duid_id);
2332		duid->duid_id = NULL;
2333		duid->duid_len = 0;
2334	}
2335	duid->duid_len = 0;
2336}
2337
2338char *
2339dhcp6optstr(type)
2340	int type;
2341{
2342	static char genstr[sizeof("opt_65535") + 1];
2343
2344	if (type > 65535)
2345		return "INVALID option";
2346
2347	switch(type) {
2348	case DH6OPT_CLIENTID:
2349		return "client ID";
2350	case DH6OPT_SERVERID:
2351		return "server ID";
2352	case DH6OPT_ORO:
2353		return "option request";
2354	case DH6OPT_PREFERENCE:
2355		return "preference";
2356	case DH6OPT_STATUS_CODE:
2357		return "status code";
2358	case DH6OPT_RAPID_COMMIT:
2359		return "rapid commit";
2360	case DH6OPT_DNS_SERVERS:
2361		return "DNS_SERVERS";
2362    /* Foxconn added start pling 09/23/2010 */
2363    case DH6OPT_DOMAIN_LIST:
2364        return "DOMAIN_LIST";
2365    /* Foxconn added end pling 09/23/2010 */
2366    /* Foxconn added start pling 01/25/2010 */
2367	case DH6OPT_SIP_SERVERS:
2368		return "SIP_SERVERS";
2369	case DH6OPT_NTP_SERVERS:
2370		return "NTP_SERVERS";
2371    /* Foxconn added end pling 01/25/2010 */
2372	default:
2373		sprintf(genstr, "opt_%d", type);
2374		return (genstr);
2375	}
2376}
2377
2378char *
2379dhcp6msgstr(type)
2380	int type;
2381{
2382	static char genstr[sizeof("msg255") + 1];
2383
2384	if (type > 255)
2385		return "INVALID msg";
2386
2387	switch(type) {
2388	case DH6_SOLICIT:
2389		return "solicit";
2390	case DH6_ADVERTISE:
2391		return "advertise";
2392	case DH6_RENEW:
2393		return "renew";
2394	case DH6_REBIND:
2395		return "rebind";
2396	case DH6_REQUEST:
2397		return "request";
2398	case DH6_REPLY:
2399		return "reply";
2400	case DH6_CONFIRM:
2401		return "confirm";
2402	case DH6_RELEASE:
2403		return "release";
2404	case DH6_DECLINE:
2405		return "decline";
2406	case DH6_INFORM_REQ:
2407		return "information request";
2408	case DH6_RECONFIGURE:
2409		return "reconfigure";
2410	case DH6_RELAY_FORW:
2411		return "relay forwarding";
2412	case DH6_RELAY_REPL:
2413		return "relay reply";
2414	default:
2415		sprintf(genstr, "msg%d", type);
2416		return (genstr);
2417	}
2418}
2419
2420char *
2421dhcp6_stcodestr(code)
2422	int code;
2423{
2424	static char genstr[sizeof("code255") + 1];
2425
2426	if (code > 255)
2427		return "INVALID code";
2428
2429	switch(code) {
2430	case DH6OPT_STCODE_SUCCESS:
2431		return "success";
2432	case DH6OPT_STCODE_UNSPECFAIL:
2433		return "unspec failure";
2434	case DH6OPT_STCODE_AUTHFAILED:
2435		return "auth fail";
2436	case DH6OPT_STCODE_ADDRUNAVAIL:
2437		return "address unavailable";
2438	case DH6OPT_STCODE_NOADDRAVAIL:
2439		return "no addresses";
2440	case DH6OPT_STCODE_NOBINDING:
2441		return "no binding";
2442	case DH6OPT_STCODE_CONFNOMATCH:
2443		return "confirm no match";
2444	case DH6OPT_STCODE_NOTONLINK:
2445		return "not on-link";
2446	case DH6OPT_STCODE_USEMULTICAST:
2447		return "use multicast";
2448	default:
2449		sprintf(genstr, "code%d", code);
2450		return (genstr);
2451	}
2452}
2453
2454char *
2455duidstr(const struct duid *duid)
2456{
2457	int i;
2458	char *cp;
2459	static char duidstr[sizeof("xx:") * 256 + sizeof("...")];
2460
2461	duidstr[0] ='\0';
2462
2463	cp = duidstr;
2464	for (i = 0; i < duid->duid_len && i <= 256; i++) {
2465		cp += sprintf(cp, "%s%02x", i == 0 ? "" : ":",
2466			      duid->duid_id[i] & 0xff);
2467	}
2468	if (i < duid->duid_len)
2469		sprintf(cp, "%s", "...");
2470
2471	return (duidstr);
2472}
2473
2474void
2475setloglevel(debuglevel)
2476	int debuglevel;
2477{
2478	if (foreground) {
2479		switch(debuglevel) {
2480		case 0:
2481			debug_thresh = LOG_ERR;
2482			break;
2483		case 1:
2484			debug_thresh = LOG_INFO;
2485			break;
2486		default:
2487			debug_thresh = LOG_DEBUG;
2488			break;
2489		}
2490	} else {
2491		switch(debuglevel) {
2492		case 0:
2493			setlogmask(LOG_UPTO(LOG_ERR));
2494			break;
2495		case 1:
2496			setlogmask(LOG_UPTO(LOG_INFO));
2497			break;
2498		}
2499	}
2500}
2501
2502void
2503dprintf(int level, const char *fmt, ...)
2504{
2505	va_list ap;
2506	char logbuf[LINE_MAX];
2507
2508	va_start(ap, fmt);
2509	vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
2510
2511	if (foreground && debug_thresh >= level) {
2512		time_t now;
2513		struct tm *tm_now;
2514		const char *month[] = {
2515			"Jan", "Feb", "Mar", "Apr", "May", "Jun",
2516			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
2517		};
2518
2519		if ((now = time(NULL)) < 0)
2520			exit(1);
2521		tm_now = localtime(&now);
2522		fprintf(stderr, "%3s/%02d/%04d %02d:%02d:%02d %s\n",
2523			month[tm_now->tm_mon], tm_now->tm_mday,
2524			tm_now->tm_year + 1900,
2525			tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
2526			logbuf);
2527	} else
2528		syslog(level, "%s", logbuf);
2529}
2530