1/*	$Id: dhcp6s.c,v 1.1.1.1 2006/12/04 00:45:25 Exp $	*/
2/*	ported from KAME: dhcp6s.c,v 1.91 2002/09/24 14:20:50 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/stat.h>
35#include <sys/socket.h>
36#include <linux/sockios.h>
37#include <sys/ioctl.h>
38#include <sys/file.h>
39
40#include <sys/uio.h>
41#if TIME_WITH_SYS_TIME
42# include <sys/time.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 <errno.h>
52
53#include <net/if.h>
54#if defined(__FreeBSD__) && __FreeBSD__ >= 3
55#include <net/if_var.h>
56#endif
57
58#include <netinet/in.h>
59
60#include <arpa/inet.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 <netdb.h>
69#include <limits.h>
70
71#include "queue.h"
72#include "timer.h"
73#include "dhcp6.h"
74#include "config.h"
75#include "common.h"
76#include "server6_conf.h"
77#include "lease.h"
78
79typedef enum { DHCP6_CONFINFO_PREFIX, DHCP6_CONFINFO_ADDRS } dhcp6_conftype_t;
80
81struct dhcp6_binding {
82	TAILQ_ENTRY(dhcp6_binding) link;
83
84	dhcp6_conftype_t type;
85	struct duid clientid;
86	void *val;
87
88	u_int32_t duration;
89	struct dhcp6_timer *timer;
90};
91
92static char *device[100];
93static int num_device = 0;
94static int debug = 0;
95const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER;
96int insock;	/* inbound udp port */
97//int outsock;	/* outbound udp port */     // Foxconn removed pling 08/15/2009
98extern FILE *server6_lease_file;
99char server6_lease_temp[100];
100
101static const struct sockaddr_in6 *sa6_any_downstream;
102static u_int16_t upstream_port;
103static struct msghdr rmh;
104static char rdatabuf[BUFSIZ];
105static int rmsgctllen;
106static char *rmsgctlbuf;
107static struct duid server_duid;
108static struct dns_list arg_dnslist;
109static struct dhcp6_timer *sync_lease_timer;
110
111struct link_decl *subnet = NULL;
112struct host_decl *host = NULL;
113struct rootgroup *globalgroup = NULL;
114
115#define DUID_FILE "/tmp/dhcp6s_duid" //Modified. "/var/lib/dhcpv6/dhcp6s_duid"
116#define DHCP6S_CONF "/etc/dhcp6s.conf"
117
118#define DH6_VALID_MESSAGE(a) \
119	(a == DH6_SOLICIT || a == DH6_REQUEST || a == DH6_RENEW || \
120	 a == DH6_REBIND || a == DH6_CONFIRM || a == DH6_RELEASE || \
121	 a == DH6_DECLINE || a == DH6_INFORM_REQ)
122
123static void usage __P((void));
124static void server6_init __P((void));
125static void server6_mainloop __P((void));
126static int server6_recv __P((int));
127static int server6_react_message __P((struct dhcp6_if *,
128				      struct in6_pktinfo *, struct dhcp6 *,
129				      struct dhcp6_optinfo *,
130				      struct sockaddr *, int));
131static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *,
132			     struct dhcp6_optinfo *,
133			     struct sockaddr *, int,
134			     struct dhcp6_optinfo *));
135static struct dhcp6_timer *check_lease_file_timo __P((void *arg));
136static struct dhcp6 *dhcp6_parse_relay __P((struct dhcp6_relay *,
137                                            struct dhcp6_relay *,
138                                            struct dhcp6_optinfo *,
139                                            struct in6_addr *));
140static int dhcp6_set_relay __P((struct dhcp6_relay *,
141                                struct dhcp6_relay *,
142                                struct dhcp6_optinfo *));
143static void dhcp6_set_relay_option_len __P((struct dhcp6_optinfo *,
144                                           int len));
145extern struct link_decl *dhcp6_allocate_link __P((struct dhcp6_if *, struct rootgroup *,
146			struct in6_addr *));
147extern struct host_decl *dhcp6_allocate_host __P((struct dhcp6_if *, struct rootgroup *,
148			struct dhcp6_optinfo *));
149
150extern int dhcp6_get_hostconf __P((struct dhcp6_optinfo *, struct dhcp6_optinfo *,
151			struct dhcp6_iaidaddr *, struct host_decl *));
152
153static void random_init(void)
154{
155	int f, n;
156	unsigned int seed = time(NULL) & getpid();
157	char rand_state[256];
158
159	f = open("/dev/urandom", O_RDONLY);
160	if (f > 0) {
161		n = read(f, rand_state, sizeof(rand_state));
162		close(f);
163		if (n > 32) {
164			initstate(seed, rand_state, n);
165			return;
166		}
167	}
168	srandom(seed);
169}
170
171int
172main(argc, argv)
173	int argc;
174	char **argv;
175{
176	int ch;
177	struct in6_addr a;
178	struct dhcp6_listval *dlv;
179	char *progname, *conffile = DHCP6S_CONF;
180
181	if ((progname = strrchr(*argv, '/')) == NULL)
182		progname = *argv;
183	else
184		progname++;
185
186	TAILQ_INIT(&arg_dnslist.addrlist);
187
188	random_init();
189
190	while ((ch = getopt(argc, argv, "c:dDfn:")) != -1) {
191		switch (ch) {
192		case 'c':
193			conffile = optarg;
194			break;
195		case 'd':
196			debug = 1;
197			break;
198		case 'D':
199			debug = 2;
200			break;
201		case 'f':
202			foreground++;
203			break;
204		case 'n':
205			warnx("-n dnsserv option was obsoleted.  "
206			    "use configuration file.");
207			if (inet_pton(AF_INET6, optarg, &a) != 1) {
208				errx(1, "invalid DNS server %s", optarg);
209				/* NOTREACHED */
210			}
211			if ((dlv = malloc(sizeof *dlv)) == NULL) {
212				errx(1, "malloc failed for a DNS server");
213				/* NOTREACHED */
214			}
215			dlv->val_addr6 = a;
216			TAILQ_INSERT_TAIL(&arg_dnslist.addrlist, dlv, link);
217			break;
218		default:
219			usage();
220			/* NOTREACHED */
221		}
222	}
223	while (optind < argc) {
224		device[num_device] = argv[optind++];
225		num_device += 1;
226	}
227
228	if (foreground == 0) {
229		if (daemon(0, 0) < 0)
230			err(1, "daemon");
231		openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
232	}
233	setloglevel(debug);
234
235	server6_init();
236	if ((server6_lease_file = init_leases(PATH_SERVER6_LEASE)) == NULL) {
237		dprintf(LOG_ERR, "%s" "failed to parse lease file",
238			FNAME);
239		exit(1);
240	}
241	strcpy(server6_lease_temp, PATH_SERVER6_LEASE);
242	strcat(server6_lease_temp, "XXXXXX");
243	server6_lease_file =
244		sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp);
245	if (server6_lease_file == NULL)
246		exit(1);
247	globalgroup = (struct rootgroup *)malloc(sizeof(struct rootgroup));
248	if (globalgroup == NULL) {
249		dprintf(LOG_ERR, "failed to allocate memory %s", strerror(errno));
250		exit(1);
251	}
252	memset(globalgroup, 0, sizeof(*globalgroup));
253	TAILQ_INIT(&globalgroup->scope.dnslist.addrlist);
254	TAILQ_INIT(&globalgroup->scope.siplist);
255	TAILQ_INIT(&globalgroup->scope.ntplist);
256	if ((sfparse(conffile)) != 0) {
257		dprintf(LOG_ERR, "%s" "failed to parse addr configuration file",
258			FNAME);
259		exit(1);
260	}
261	server6_mainloop();
262	exit(0);
263}
264
265static void
266usage()
267{
268	fprintf(stderr,
269		"usage: dhcp6s [-c configfile] [-dDf] [interface]\n");
270	exit(0);
271}
272
273/*------------------------------------------------------------*/
274
275void
276server6_init()
277{
278	struct addrinfo hints;
279	struct addrinfo *res, *res2;
280	int error, skfd, i;
281	int on = 1;
282	int ifidx[MAX_DEVICE];
283	struct ipv6_mreq mreq6;
284	static struct iovec iov;
285	static struct sockaddr_in6 sa6_any_downstream_storage;
286	char buff[1024];
287	struct ifconf ifc;
288	struct ifreq *ifr;
289	double d;
290	struct timeval timo;
291	/* initialize inbound socket */
292	memset(&hints, 0, sizeof(hints));
293	hints.ai_family = PF_INET6;
294	hints.ai_socktype = SOCK_DGRAM;
295	hints.ai_protocol = IPPROTO_UDP;
296	hints.ai_flags = AI_PASSIVE;
297	error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res);
298	if (error) {
299		dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
300			FNAME, gai_strerror(error));
301		exit(1);
302	}
303	insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
304	if (insock < 0) {
305		dprintf(LOG_ERR, "%s" "socket(insock): %s",
306			FNAME, strerror(errno));
307		exit(1);
308	}
309#ifdef IPV6_RECVPKTINFO
310	if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
311		       sizeof(on)) < 0) {
312		dprintf(LOG_ERR, "%s"
313			"setsockopt(inbound, IPV6_RECVPKTINFO): %s",
314			FNAME, strerror(errno));
315		exit(1);
316	}
317#else
318	if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
319		       sizeof(on)) < 0) {
320		dprintf(LOG_ERR, "%s"
321			"setsockopt(inbound, IPV6_PKTINFO): %s",
322			FNAME, strerror(errno));
323		exit(1);
324	}
325#endif
326	if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) {
327		dprintf(LOG_ERR, "%s" "dhcp6s: bind(insock): %s",
328			FNAME, strerror(errno));
329		exit(1);
330	}
331	upstream_port = ((struct sockaddr_in6 *) res->ai_addr)->sin6_port;
332	freeaddrinfo(res);
333
334	/* initiallize outbound interface */
335	hints.ai_flags = AI_PASSIVE;
336	error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res);
337	if (error) {
338		dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
339			FNAME, gai_strerror(error));
340		exit(1);
341	}
342    /* Foxconn removed start pling 08/15/2009 */
343    /* Don't use outsock as it  conflicts with dhcp6c */
344#if 0
345	outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
346	if (outsock < 0) {
347		dprintf(LOG_ERR, "%s" "socket(outsock): %s",
348			FNAME, strerror(errno));
349		exit(1);
350	}
351	if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) {
352		dprintf(LOG_ERR, "%s" "bind(outsock): %s",
353			FNAME, strerror(errno));
354		exit(1);
355	}
356#endif
357    /* Foxconn removed end pling 08/15/2009 */
358	memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen);
359	sa6_any_downstream =
360		(const struct sockaddr_in6*)&sa6_any_downstream_storage;
361	freeaddrinfo(res);
362
363	/* initialize send/receive buffer */
364	iov.iov_base = (caddr_t)rdatabuf;
365	iov.iov_len = sizeof(rdatabuf);
366	rmh.msg_iov = &iov;
367	rmh.msg_iovlen = 1;
368	rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
369	if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) {
370		dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME);
371		exit(1);
372	}
373	if (num_device != 0) {
374		for (i = 0; i < num_device; i++) {
375			ifidx[i] = if_nametoindex(device[i]);
376			if (ifidx[i] == 0) {
377				dprintf(LOG_ERR, "%s"
378					"invalid interface %s", FNAME, device[0]);
379				exit(1);
380			}
381			ifinit(device[i]);
382		}
383		if (get_duid(DUID_FILE, device[0], &server_duid)) {
384			dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME);
385			exit(1);
386		}
387	} else {
388		/* all the interfaces join multicast group */
389		ifc.ifc_len = sizeof(buff);
390		ifc.ifc_buf = buff;
391		if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
392			dprintf(LOG_ERR, "new socket failed");
393			exit(1);
394		}
395		if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
396			dprintf(LOG_ERR, "SIOCGIFCONF: %s\n", strerror(errno));
397			exit(1);
398		}
399		ifr = ifc.ifc_req;
400		for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) {
401			dprintf(LOG_DEBUG, "found device %s", ifr->ifr_name);
402			ifidx[num_device] = if_nametoindex(ifr->ifr_name);
403			if (ifidx[num_device] < 0) {
404				dprintf(LOG_ERR, "%s: unknown interface.\n",
405					ifr->ifr_name);
406				continue;
407			}
408			dprintf(LOG_DEBUG, "if %s index is %d", ifr->ifr_name,
409				ifidx[num_device]);
410#ifdef mshirley
411			if (((ifr->ifr_flags & IFF_UP) == 0)) continue;
412#endif
413			if (strcmp(ifr->ifr_name, "lo")) {
414				/* get our DUID */
415				if (get_duid(DUID_FILE, ifr->ifr_name, &server_duid)) {
416					dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME);
417					exit(1);
418				}
419			}
420			ifinit(ifr->ifr_name);
421			num_device += 1;
422		}
423	}
424	for (i = 0; i < num_device; i++) {
425		hints.ai_flags = 0;
426		error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2);
427		if (error) {
428			dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
429				FNAME, gai_strerror(error));
430			exit(1);
431		}
432		memset(&mreq6, 0, sizeof(mreq6));
433		mreq6.ipv6mr_interface = ifidx[i];
434		memcpy(&mreq6.ipv6mr_multiaddr,
435	    		&((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
436	    		sizeof(mreq6.ipv6mr_multiaddr));
437		if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
438		    &mreq6, sizeof(mreq6))) {
439			dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP) %s",
440				FNAME, strerror(errno));
441			exit(1);
442		}
443		freeaddrinfo(res2);
444
445		hints.ai_flags = 0;
446		error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM,
447				    &hints, &res2);
448		if (error) {
449			dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
450				FNAME, gai_strerror(error));
451			exit(1);
452		}
453		memset(&mreq6, 0, sizeof(mreq6));
454		mreq6.ipv6mr_interface = ifidx[i];
455		memcpy(&mreq6.ipv6mr_multiaddr,
456	    		&((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
457	    		sizeof(mreq6.ipv6mr_multiaddr));
458		if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
459		    &mreq6, sizeof(mreq6))) {
460			dprintf(LOG_ERR,
461				"%s" "setsockopt(insock, IPV6_JOIN_GROUP): %s",
462				FNAME, strerror(errno));
463			exit(1);
464		}
465		freeaddrinfo(res2);
466
467        /* Foxconn removed start pling 08/15/2009 */
468        /* Don't use outsock as it  conflicts with dhcp6c */
469#if 0
470		/* set outgoing interface of multicast packets for DHCP reconfig */
471		if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
472		    &ifidx[i], sizeof(ifidx[i])) < 0) {
473			dprintf(LOG_ERR,
474				"%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s",
475				FNAME, strerror(errno));
476			exit(1);
477		}
478#endif
479        /* Foxconn removed start pling 08/15/2009 */
480	}
481	/* set up sync lease file timer */
482	sync_lease_timer = dhcp6_add_timer(check_lease_file_timo, NULL);
483	d = DHCP6_SYNCFILE_TIME;
484	timo.tv_sec = (long)d;
485	timo.tv_usec = 0;
486	dprintf(LOG_DEBUG, "set timer for syncing file ...");
487	dhcp6_set_timer(&timo, sync_lease_timer);
488	return;
489}
490
491
492static void
493server6_mainloop()
494{
495	struct timeval *w;
496	int ret;
497	fd_set r;
498
499	while (1) {
500		w = dhcp6_check_timer();
501
502		FD_ZERO(&r);
503		FD_SET(insock, &r);
504		ret = select(insock + 1, &r, NULL, NULL, w);
505		switch (ret) {
506		case -1:
507			dprintf(LOG_ERR, "%s" "select: %s",
508				FNAME, strerror(errno));
509			exit(1);
510			/* NOTREACHED */
511		case 0:		/* timeout */
512			break;
513		default:
514			break;
515		}
516		if (FD_ISSET(insock, &r))
517			server6_recv(insock);
518	}
519}
520
521static int
522server6_recv(s)
523	int s;
524{
525	ssize_t len;
526	struct sockaddr_storage from;
527	int fromlen;
528	struct msghdr mhdr;
529	struct iovec iov;
530	char cmsgbuf[BUFSIZ];
531	struct cmsghdr *cm;
532	struct in6_pktinfo *pi = NULL;
533	struct dhcp6_if *ifp;
534	struct dhcp6 *dh6;
535	struct dhcp6_optinfo optinfo;
536	struct in6_addr relay;  /* the address of the first relay, if any */
537	memset(&iov, 0, sizeof(iov));
538	memset(&mhdr, 0, sizeof(mhdr));
539
540	iov.iov_base = rdatabuf;
541	iov.iov_len = sizeof(rdatabuf);
542	mhdr.msg_name = &from;
543	mhdr.msg_namelen = sizeof(from);
544	mhdr.msg_iov = &iov;
545	mhdr.msg_iovlen = 1;
546	mhdr.msg_control = (caddr_t)cmsgbuf;
547	mhdr.msg_controllen = sizeof(cmsgbuf);
548
549	if ((len = recvmsg(insock, &mhdr, 0)) < 0) {
550		dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno));
551		return -1;
552	}
553	fromlen = mhdr.msg_namelen;
554
555	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm;
556	     cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) {
557		if (cm->cmsg_level == IPPROTO_IPV6 &&
558		    cm->cmsg_type == IPV6_PKTINFO &&
559		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
560			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
561		}
562	}
563	if (pi == NULL) {
564		dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME);
565		return -1;
566	}
567	dprintf(LOG_DEBUG, "received message packet info addr is %s, scope id (%d)",
568	    in6addr2str(&pi->ipi6_addr, 0), (unsigned int)pi->ipi6_ifindex);
569	if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) {
570		dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME,
571		    (unsigned int)pi->ipi6_ifindex);
572		return -1;
573	}
574	/* Foxconn added start pling 06/04/2014 */
575	/* Don't accept packets not coming from LAN,
576	 *  e.g. from router's own dhcp6c client */
577	if (ifp && strcmp(ifp->ifname, "br0")) {
578		dprintf(LOG_INFO, "%s" "Don't accept pkts from non-LAN interface (%s)", FNAME, ifp->ifname);
579		return -1;
580	}
581	/* Foxconn added end pling 06/04/2014 */
582	if (len < sizeof(*dh6)) {
583		dprintf(LOG_INFO, "%s" "short packet", FNAME);
584		return -1;
585	}
586
587	dh6 = (struct dhcp6 *)rdatabuf;
588
589	dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME,
590	    dhcp6msgstr(dh6->dh6_msgtype),
591	    addr2str((struct sockaddr *)&from));
592
593	dhcp6_init_options(&optinfo);
594
595	/*
596	 * If this is a relayed message, parse all of the relay data, storing
597	 * the link addresses, peer addresses, and interface identifiers for
598	 * later use. Get a pointer to the original client message.
599	 */
600	if (dh6->dh6_msgtype == DH6_RELAY_FORW) {
601		dh6 = dhcp6_parse_relay((struct dhcp6_relay *) dh6,
602		                        (struct dhcp6_relay *) (rdatabuf + len),
603		                        &optinfo, &relay);
604
605		/*
606		 * NULL means there was an error in the relay format or no
607		 * client message was found.
608		 */
609		if (dh6 == NULL) {
610			dprintf(LOG_INFO, "%s" "failed to parse relay fields "
611			                       "or could not find client message", FNAME);
612			return -1;
613		}
614	}
615
616	/*
617	 * parse and validate options in the request
618	 */
619    /* Foxconn modified start pling 10/04/2010 */
620    /* Pass extra arg to 'dhcp6_get_options' */
621#if 0
622	if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
623	    (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) {
624#endif
625    if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
626        (struct dhcp6opt *)(rdatabuf + len), &optinfo, 0, 0, 0) < 0) {
627    /* Foxconn modified end pling 10/04/2010 */
628		dprintf(LOG_INFO, "%s" "failed to parse options", FNAME);
629		return -1;
630	}
631	/* check host decl first */
632	host = dhcp6_allocate_host(ifp, globalgroup, &optinfo);
633	/* ToDo: allocate subnet after relay agent done
634	 * now assume client is on the same link as server
635	 * if the subnet couldn't be found return status code NotOnLink to client
636	 */
637	/*
638	 * If the relay list is empty, then this is a message received directly
639	 * from the client, so client is on the same link as the server.
640	 * Otherwise, allocate the client an address based on the first relay
641	 * that forwarded the message.
642	 */
643	if (TAILQ_EMPTY(&optinfo.relay_list))
644		subnet = dhcp6_allocate_link(ifp, globalgroup, NULL);
645	else
646		subnet = dhcp6_allocate_link(ifp, globalgroup, &relay);
647
648	if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype)))
649		dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s",
650		    FNAME, dhcp6msgstr(dh6->dh6_msgtype));
651	else
652		server6_react_message(ifp, pi, dh6, &optinfo,
653			(struct sockaddr *)&from, fromlen);
654	dhcp6_clear_options(&optinfo);
655	return 0;
656}
657
658static int
659server6_react_message(ifp, pi, dh6, optinfo, from, fromlen)
660	struct dhcp6_if *ifp;
661	struct in6_pktinfo *pi;
662	struct dhcp6 *dh6;
663	struct dhcp6_optinfo *optinfo;
664	struct sockaddr *from;
665	int fromlen;
666{
667	struct dhcp6_optinfo roptinfo;
668
669	int addr_flag = 0;
670	int addr_request = 0;
671	int resptype = DH6_REPLY;
672	int num = DH6OPT_STCODE_SUCCESS;
673	int sending_hint = 0;
674
675	/* message validation according to Section 18.2 of dhcpv6-28 */
676
677	/* the message must include a Client Identifier option */
678	if (optinfo->clientID.duid_len == 0) {
679		dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
680		return -1;
681	} else {
682		dprintf(LOG_DEBUG, "%s" "client ID %s", FNAME,
683			duidstr(&optinfo->clientID));
684	}
685	/* the message must include a Server Identifier option in below messages*/
686	switch (dh6->dh6_msgtype) {
687	case DH6_REQUEST:
688	case DH6_RENEW:
689        case DH6_DECLINE:
690	case DH6_RELEASE:
691		if (optinfo->serverID.duid_len == 0) {
692			dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
693			return -1;
694		}
695		/* the contents of the Server Identifier option must match ours */
696		if (duidcmp(&optinfo->serverID, &server_duid)) {
697			dprintf(LOG_INFO, "server ID %s mismatch %s",
698				duidstr(&optinfo->serverID), duidstr(&server_duid));
699			return -1;
700		}
701		break;
702	default:
703		break;
704	}
705	/*
706	 * configure necessary options based on the options in request.
707	 */
708	dhcp6_init_options(&roptinfo);
709	/* server information option */
710	if (duidcpy(&roptinfo.serverID, &server_duid)) {
711		dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME);
712		goto fail;
713	}
714	/* copy client information back */
715	if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
716		dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME);
717		goto fail;
718	}
719	/* if the client is not on the link */
720	if (host == NULL && subnet == NULL) {
721		num = DH6OPT_STCODE_NOTONLINK;
722		/* Draft-28 18.2.2, drop the message if NotOnLink */
723		if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_REBIND)
724			goto fail;
725		else
726			goto send;
727	}
728	if (subnet) {
729		roptinfo.pref = subnet->linkscope.server_pref;
730		roptinfo.flags = (optinfo->flags & subnet->linkscope.allow_flags) |
731				subnet->linkscope.send_flags;
732		dnslist = subnet->linkscope.dnslist;
733		siplist = subnet->linkscope.siplist;
734		ntplist = subnet->linkscope.ntplist;
735	}
736	if (host) {
737		roptinfo.pref = host->hostscope.server_pref;
738		roptinfo.flags = (optinfo->flags & host->hostscope.allow_flags) |
739				host->hostscope.send_flags;
740		dnslist = host->hostscope.dnslist;
741		siplist = host->hostscope.siplist;
742		ntplist = host->hostscope.ntplist;
743	}
744	/* prohibit a mixture of old and new style of DNS server config */
745	if (!TAILQ_EMPTY(&arg_dnslist.addrlist)) {
746		if (!TAILQ_EMPTY(&dnslist.addrlist)) {
747			dprintf(LOG_INFO, "%s" "do not specify DNS servers "
748			    "both by command line and by configuration file.",
749			    FNAME);
750			exit(1);
751		}
752		dnslist = arg_dnslist;
753		TAILQ_INIT(&arg_dnslist.addrlist);
754	}
755	dprintf(LOG_DEBUG, "server preference is %2x", roptinfo.pref);
756	if (roptinfo.flags & DHCIFF_UNICAST) {
757		/* todo find the right server unicast address to client*/
758		/* get_linklocal(device, &roptinfo.server_addr) */
759		memcpy(&roptinfo.server_addr, &ifp->linklocal,
760		       sizeof(roptinfo.server_addr));
761		dprintf(LOG_DEBUG, "%s" "server address is %s",
762			FNAME, in6addr2str(&roptinfo.server_addr, 0));
763	}
764	/*
765	 * When the server receives a Request message via unicast from a
766	 * client to which the server has not sent a unicast option, the server
767	 * discards the Request message and responds with a Reply message
768	 * containing a Status Code option with value UseMulticast, a Server
769	 * Identifier option containing the server's DUID, the Client
770	 * Identifier option from the client message and no other options.
771	 * [dhcpv6-26 18.2.1]
772	 */
773	switch (dh6->dh6_msgtype) {
774	case DH6_REQUEST:
775	case DH6_RENEW:
776	case DH6_DECLINE:
777		/*
778		 * If the message was relayed, then do not check whether the message
779		 * came in via unicast or multicast, since the relay may be configured
780		 * to send messages via unicast.
781		 */
782		if (TAILQ_EMPTY(&optinfo->relay_list) &&
783		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
784			if (!(roptinfo.flags & DHCIFF_UNICAST)) {
785				num = DH6OPT_STCODE_USEMULTICAST;
786				goto send;
787			} else
788				break;
789		}
790		break;
791	default:
792		/*
793		 * If the message was relayed, then do not check whether the message
794		 * came in via unicast or multicast, since the relay may be configured
795		 * to send messages via unicast.
796		 */
797		if (TAILQ_EMPTY(&optinfo->relay_list) &&
798		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
799			num = DH6OPT_STCODE_USEMULTICAST;
800			goto send;
801		}
802		break;
803	}
804
805	switch (dh6->dh6_msgtype) {
806	case DH6_SOLICIT:
807		/*
808		 * If the client has included a Rapid Commit option and the
809		 * server has been configured to respond with committed address
810		 * assignments and other resources, responds to the Solicit
811		 * with a Reply message.
812		 * [dhcpv6-28 Section 17.2.1]
813		 * [dhcpv6-28 Section 17.2.2]
814		 * If Solicit has IA option, responds to Solicit with a Advertise
815		 * message.
816		 */
817		if (/*optinfo->iaidinfo.iaid != 0 &&*/ !(roptinfo.flags & DHCIFF_INFO_ONLY)) {  /* pling modified 06/03/2014, for iOS compatibility */
818			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
819					sizeof(roptinfo.iaidinfo));
820			roptinfo.type = optinfo->type;
821			dprintf(LOG_DEBUG, "option type is %d", roptinfo.type);
822			addr_request = 1;
823			if (roptinfo.flags & DHCIFF_RAPID_COMMIT) {
824				resptype = DH6_REPLY;
825			} else {
826				resptype = DH6_ADVERTISE;
827				/* giving hint ?? */
828				sending_hint = 1;
829			}
830		}
831		break;
832	case DH6_INFORM_REQ:
833		/* don't response to info-req if there is any IA option */
834		if (optinfo->iaidinfo.iaid != 0)
835			goto fail;
836		/* DNS server */
837		if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
838			dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
839			goto fail;
840		}
841		/* Foxconn added start pling 01/25/2010 */
842        /* Add SIP and NTP server if available */
843		if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) {
844			dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME);
845			goto fail;
846		}
847		if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) {
848			dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME);
849			goto fail;
850		}
851		/* Foxconn added end pling 01/25/2010 */
852		roptinfo.dns_list.domainlist = dnslist.domainlist;
853		break;
854	case DH6_REQUEST:
855		/* get iaid for that request client for that interface */
856		if (/*optinfo->iaidinfo.iaid != 0 &&*/ !(roptinfo.flags & DHCIFF_INFO_ONLY)) { /* pling modified 06/03/2014, for iOS compatibility */
857			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
858					sizeof(roptinfo.iaidinfo));
859			roptinfo.type = optinfo->type;
860			addr_request = 1;
861		}
862		break;
863	/*
864	 * Locates the client's binding and verifies that the information
865	 * from the client matches the information stored for that client.
866	 */
867	case DH6_RENEW:
868	case DH6_REBIND:
869	case DH6_DECLINE:
870	case DH6_RELEASE:
871	case DH6_CONFIRM:
872		roptinfo.type = optinfo->type;
873		if (dh6->dh6_msgtype == DH6_RENEW || dh6->dh6_msgtype == DH6_REBIND)
874			addr_flag = ADDR_UPDATE;
875		if (dh6->dh6_msgtype == DH6_RELEASE)
876			addr_flag = ADDR_REMOVE;
877		/* Foxconn Bob modified start on 01/09/2015, include DNS option in the reply of renew packet,
878		   or some win8 PC can not get DNS server address correctly in TEC's noise test environment */
879		if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_RENEW)  {
880		/* Foxconn Bob modified end on 01/09/2015 */
881			/* DNS server */
882			addr_flag = ADDR_VALIDATE;
883			if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
884				dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
885				goto fail;
886			}
887			roptinfo.dns_list.domainlist = dnslist.domainlist;
888		}
889		if (dh6->dh6_msgtype == DH6_DECLINE)
890			addr_flag = ADDR_ABANDON;
891	if (/*optinfo->iaidinfo.iaid != 0*/ 1) {          /* pling modified 06/03/2014, for iOS compatibility */
892		if (!TAILQ_EMPTY(&optinfo->addr_list) && resptype != DH6_ADVERTISE) {
893			struct dhcp6_iaidaddr *iaidaddr;
894			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
895					sizeof(roptinfo.iaidinfo));
896			roptinfo.type = optinfo->type;
897			/* find bindings */
898			if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) == NULL) {
899				if (dh6->dh6_msgtype == DH6_REBIND)
900					goto fail;
901				num = DH6OPT_STCODE_NOBINDING;
902				dprintf(LOG_INFO, "%s" "Nobinding for client %s iaid %u",
903					FNAME, duidstr(&optinfo->clientID),
904						optinfo->iaidinfo.iaid);
905				break;
906			}
907			if (addr_flag != ADDR_UPDATE) {
908				dhcp6_copy_list(&roptinfo.addr_list, &optinfo->addr_list);
909			} else {
910				/* get static host configuration */
911				if (host)
912					dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host);
913				/* allow dynamic address assginment for the host too */
914				if (optinfo->type == IAPD)
915					dhcp6_create_prefixlist(&roptinfo,
916								optinfo,
917								iaidaddr,
918								subnet);
919				else
920					dhcp6_create_addrlist(&roptinfo, optinfo,
921							iaidaddr, subnet);
922				/* in case there is not bindings available */
923				if (TAILQ_EMPTY(&roptinfo.addr_list)) {
924					num = DH6OPT_STCODE_NOBINDING;
925					dprintf(LOG_INFO, "%s"
926					    "Bindings are not on link for client %s iaid %u",
927						FNAME, duidstr(&optinfo->clientID),
928						roptinfo.iaidinfo.iaid);
929					break;
930				}
931			}
932			if (addr_flag == ADDR_VALIDATE) {
933				if (dhcp6_validate_bindings(&roptinfo, iaidaddr))
934					num = DH6OPT_STCODE_NOBINDING;
935				break;
936			} else {
937				/* do update if this is not a confirm */
938				if (dhcp6_update_iaidaddr(&roptinfo, addr_flag)
939						!= 0) {
940					dprintf(LOG_INFO, "%s"
941						"bindings failed for client %s iaid %u",
942						FNAME, duidstr(&optinfo->clientID),
943							roptinfo.iaidinfo.iaid);
944					num = DH6OPT_STCODE_UNSPECFAIL;
945					break;
946				}
947			}
948			num = DH6OPT_STCODE_SUCCESS;
949		} else
950			num = DH6OPT_STCODE_NOADDRAVAIL;
951	} else
952		dprintf(LOG_ERR, "invalid message type");
953		break;
954	default:
955		break;
956	}
957	/*
958	 * If the Request message contained an Option Request option, the
959	 * server MUST include options in the Reply message for any options in
960	 * the Option Request option the server is configured to return to the
961	 * client.
962	 * [dhcpv6-26 18.2.1]
963	 * Note: our current implementation always includes all information
964	 * that we can provide.  So we do not have to check the option request
965	 * options.
966	 */
967	if (addr_request == 1) {
968		int found_binding = 0;
969		struct dhcp6_iaidaddr *iaidaddr;
970		/* find bindings */
971		if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) != NULL) {
972			found_binding = 1;
973			addr_flag = ADDR_UPDATE;
974		}
975		if (host)
976			dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host);
977		/* valid and create addresses list */
978		if (optinfo->type == IAPD)
979			dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet);
980		else
981			dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet);
982		if (TAILQ_EMPTY(&roptinfo.addr_list)) {
983			num = DH6OPT_STCODE_NOADDRAVAIL;
984		} else if (sending_hint == 0) {
985		/* valid client request address list */
986			if (found_binding) {
987			       if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) {
988					dprintf(LOG_ERR,
989					"assigned ipv6address for client iaid %u failed",
990						roptinfo.iaidinfo.iaid);
991					num = DH6OPT_STCODE_UNSPECFAIL;
992			       } else
993					num = DH6OPT_STCODE_SUCCESS;
994			} else {
995			       	if (dhcp6_add_iaidaddr(&roptinfo) != 0) {
996					dprintf(LOG_ERR,
997					"assigned ipv6address for client iaid %u failed",
998						roptinfo.iaidinfo.iaid);
999					num = DH6OPT_STCODE_UNSPECFAIL;
1000				} else
1001					num = DH6OPT_STCODE_SUCCESS;
1002			}
1003		}
1004		/* DNS server */
1005		if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
1006			dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
1007			goto fail;
1008		}
1009
1010        /* Foxconn added start pling 01/25/2010 */
1011		if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) {
1012			dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME);
1013			goto fail;
1014		}
1015		if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) {
1016			dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME);
1017			goto fail;
1018		}
1019        /* Foxconn added end pling 01/25/2010 */
1020
1021		roptinfo.dns_list.domainlist = dnslist.domainlist;
1022	}
1023	/* add address status code */
1024  send:
1025	dprintf(LOG_DEBUG, " status code: %s", dhcp6_stcodestr(num));
1026	if (dhcp6_add_listval(&roptinfo.stcode_list,
1027	   	&num, DHCP6_LISTVAL_NUM) == NULL) {
1028		dprintf(LOG_ERR, "%s" "failed to copy "
1029	    		"status code", FNAME);
1030		goto fail;
1031	}
1032	/* send a reply message. */
1033    if (num != DH6OPT_STCODE_NOADDRAVAIL)   /* pling added, Comcast issue workaround */
1034	(void)server6_send(resptype, ifp, dh6, optinfo, from, fromlen,
1035			   &roptinfo);
1036
1037	dhcp6_clear_options(&roptinfo);
1038	return 0;
1039
1040  fail:
1041	dhcp6_clear_options(&roptinfo);
1042	return -1;
1043}
1044
1045/* Foxconn added start pling 04/13/2015 */
1046/* to lookup the MAC address from link local address via "ip -6 neigh" command */
1047static int get_client_mac(char *link_local_addr, char *mac)
1048{
1049	FILE *fp = NULL;
1050	char line[256];
1051	char *tmp;
1052	int  found = 0;
1053
1054	system("/usr/sbin/ip -6 neigh > /tmp/ip6_neigh");
1055	fp = fopen("/tmp/ip6_neigh", "r");
1056	if (fp) {
1057		while (!feof(fp)) {
1058			fgets(line, sizeof(line), fp);
1059			if (strstr(line, link_local_addr)) {
1060				tmp = strstr(line, "lladdr");
1061                if(tmp) {
1062    				tmp += strlen("lladdr") + 1;
1063	    			memcpy(mac, tmp, 17);
1064		    		found = 1;
1065				    break;
1066                }
1067			}
1068		}
1069		fclose(fp);
1070	}
1071	unlink("/tmp/ip6_neigh");
1072
1073	return found;
1074}
1075/* Foxconn added end pling 04/13/2015 */
1076
1077static int
1078server6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo)
1079	int type;
1080	struct dhcp6_if *ifp;
1081	struct dhcp6 *origmsg;
1082	struct dhcp6_optinfo *optinfo, *roptinfo;
1083	struct sockaddr *from;
1084	int fromlen;
1085{
1086	char replybuf[BUFSIZ];
1087	struct sockaddr_in6 dst;
1088	int len, optlen, relaylen = 0;
1089	struct dhcp6 *dh6;
1090
1091	if (sizeof(struct dhcp6) > sizeof(replybuf)) {
1092		dprintf(LOG_ERR, "%s" "buffer size assumption failed", FNAME);
1093		return (-1);
1094	}
1095
1096	if (!TAILQ_EMPTY(&optinfo->relay_list) &&
1097	    (relaylen = dhcp6_set_relay((struct dhcp6_relay *) replybuf,
1098	                                (struct dhcp6_relay *) (replybuf +
1099	                                                        sizeof (replybuf)),
1100	                                optinfo)) < 0) {
1101		dprintf(LOG_INFO, "%s" "failed to construct relay message", FNAME);
1102		return (-1);
1103	}
1104
1105	dh6 = (struct dhcp6 *) (replybuf + relaylen);
1106	len = sizeof(*dh6);
1107	memset(dh6, 0, sizeof(*dh6));
1108	dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid;
1109	dh6->dh6_msgtype = (u_int8_t)type;
1110
1111	/* set options in the reply message */
1112	if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1),
1113					(struct dhcp6opt *)(replybuf +
1114							    sizeof(replybuf)),
1115					roptinfo)) < 0) {
1116		dprintf(LOG_INFO, "%s" "failed to construct reply options",
1117			FNAME);
1118		return (-1);
1119	}
1120	len += optlen;
1121
1122	/*
1123	 * If there were any Relay Message options, fill in the option-len
1124	 * field(s) with the appropriate value(s).
1125	 */
1126	if (!TAILQ_EMPTY(&optinfo->relay_list))
1127	    dhcp6_set_relay_option_len(optinfo, len);
1128
1129	len += relaylen;
1130
1131	/* specify the destination and send the reply */
1132	dst = *sa6_any_downstream;
1133	dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr;
1134
1135	/* RELAY-REPL messages need to be directed back to the port the relay
1136	   agent is listening on, namely DH6PORT_UPSTREAM */
1137	if (relaylen > 0)
1138		dst.sin6_port = upstream_port;
1139
1140	dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id;
1141	dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d",
1142		addr2str((struct sockaddr *)&dst), dst.sin6_scope_id);
1143    /* Foxconn modified start pling 08/15/2009 */
1144    /* why use 'outsock' to send to client? */
1145	//if (transmit_sa(outsock, &dst, replybuf, len) != 0) {
1146	if (transmit_sa(insock, &dst, replybuf, len) != 0) {
1147    /* Foxconn modified end pling 08/15/2009 */
1148		dprintf(LOG_ERR, "%s" "transmit %s to %s failed", FNAME,
1149			dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
1150		return (-1);
1151	}
1152
1153	dprintf(LOG_DEBUG, "%s" "transmit %s to %s", FNAME,
1154		dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
1155
1156	/* Foxconn added start pling 04/13/2015 */
1157	/* R7000 TD#485: workaround for Mac OS client.
1158	 *  Mac OS does not respond NA, so add static neigh entry */
1159	if (type == DH6_REPLY)
1160	{
1161		char command[256];
1162		unsigned char client_mac[32];
1163		unsigned char link_local_addr[128];
1164		struct dhcp6_listval *dp;
1165
1166		memset(&client_mac, 0, sizeof(client_mac));
1167		if (!TAILQ_EMPTY(&roptinfo->addr_list)) {
1168			for (dp = TAILQ_FIRST(&roptinfo->addr_list); dp;
1169				 dp = TAILQ_NEXT(dp, link)) {
1170				sprintf(link_local_addr, "%s", addr2str((struct sockaddr *)&dst));
1171				dprintf(LOG_DEBUG, "%s" "Global address is %s, lladdr is %s " , FNAME,
1172					in6addr2str(&dp->val_dhcp6addr.addr,0), link_local_addr);
1173				if (get_client_mac(link_local_addr, client_mac)) {
1174					sprintf(command, "/usr/sbin/ip -6 neigh replace %s lladdr %s dev br0",
1175							in6addr2str(&dp->val_dhcp6addr.addr,0), client_mac);
1176					dprintf(LOG_DEBUG, "%s" "Command is '%s' ", FNAME, command);
1177					system(command);
1178				}
1179			}
1180		}
1181	}
1182	/* Foxconn added end pling 04/13/2015 */
1183
1184	return 0;
1185}
1186
1187static struct dhcp6_timer
1188*check_lease_file_timo(void *arg)
1189{
1190	double d;
1191	struct timeval timo;
1192	struct stat buf;
1193	FILE *file;
1194	stat(PATH_SERVER6_LEASE, &buf);
1195	strcpy(server6_lease_temp, PATH_SERVER6_LEASE);
1196	strcat(server6_lease_temp, "XXXXXX");
1197	if (buf.st_size > MAX_FILE_SIZE) {
1198		file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp);
1199		if (file != NULL)
1200			server6_lease_file = file;
1201	}
1202	d = DHCP6_SYNCFILE_TIME;
1203	timo.tv_sec = (long)d;
1204	timo.tv_usec = 0;
1205	dhcp6_set_timer(&timo, sync_lease_timer);
1206	return sync_lease_timer;
1207}
1208
1209/*
1210 * Parse all of the RELAY-FORW messages and interface ID options. Each
1211 * RELAY-FORW messages will have its hop count, link address, peer-address,
1212 * and interface ID (if any) put into a relay_listval structure.
1213 * A pointer to the actual original client message will be returned.
1214 * If this client message cannot be found, NULL is returned to signal an error.
1215 */
1216static struct dhcp6 *
1217dhcp6_parse_relay(relay_msg, endptr, optinfo, relay_addr)
1218	struct dhcp6_relay *relay_msg;
1219	struct dhcp6_relay *endptr;
1220	struct dhcp6_optinfo *optinfo;
1221	struct in6_addr *relay_addr;
1222{
1223	struct relay_listval *relay_val;
1224	struct dhcp6 *relayed_msg;  /* the original message that the relay
1225	                               received */
1226	struct dhcp6opt *option, *option_endptr = (struct dhcp6opt *) endptr;
1227
1228	u_int16_t optlen;
1229	u_int16_t opt;
1230
1231	while ((relay_msg + 1) < endptr) {
1232		relay_val = (struct relay_listval *)
1233		            calloc (1, sizeof (struct relay_listval));
1234
1235		if (relay_val == NULL) {
1236			dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
1237			relayfree(&optinfo->relay_list);
1238			return NULL;
1239		}
1240
1241		/* copy the msg-type, hop-count, link-address, and peer-address */
1242		memcpy (&relay_val->relay, relay_msg, sizeof (struct dhcp6_relay));
1243
1244		/* set the msg type to relay reply now so that it doesn't need to be
1245		   done when formatting the reply */
1246		relay_val->relay.dh6_msg_type = DH6_RELAY_REPL;
1247
1248		TAILQ_INSERT_TAIL(&optinfo->relay_list, relay_val, link);
1249
1250		/*
1251		 * need to record the first relay's link address field for later use.
1252		 * The first relay is the last one we see, so keep overwriting the
1253		 * relay value.
1254		 */
1255		memcpy (relay_addr, &relay_val->relay.link_addr,
1256		        sizeof (struct in6_addr));
1257
1258		/* now handle the options in the RELAY-FORW message */
1259		/*
1260		 * The only options that should appear in a RELAY-FORW message are:
1261		 * - Interface identifier
1262		 * - Relay message
1263		 *
1264		 * All other options are ignored.
1265		 */
1266		option = (struct dhcp6opt *) (relay_msg + 1);
1267
1268		relayed_msg = NULL; /* if this is NULL at the end of the loop, no
1269		                       relayed message was found */
1270
1271		/* since the order of options is not specified, all of the options
1272		   must be processed */
1273		while ((option + 1) < option_endptr) {
1274			memcpy (&opt, &option->dh6opt_type, sizeof(opt));
1275			opt = ntohs(opt);
1276			memcpy (&optlen, &option->dh6opt_len, sizeof(optlen));
1277			optlen = ntohs(optlen);
1278
1279			if ((char *) (option + 1) + optlen > (char *) option_endptr) {
1280				dprintf(LOG_ERR, "%s" "invalid option length in %s option",
1281				        FNAME, dhcp6optstr(opt));
1282				relayfree(&optinfo->relay_list);
1283				return NULL;
1284			}
1285
1286			if (opt == DH6OPT_INTERFACE_ID) {
1287				/* if this is not the first interface identifier option,
1288				   then the message is incorrectly formed */
1289				if (relay_val->intf_id == NULL) {
1290					if (optlen) {
1291						relay_val->intf_id = (struct intf_id *)
1292						                     malloc (sizeof (struct intf_id));
1293						if (relay_val->intf_id == NULL) {
1294							dprintf(LOG_ERR, "%s" "failed to allocate memory",
1295							        FNAME);
1296							relayfree(&optinfo->relay_list);
1297							return NULL;
1298						}
1299						else {
1300							relay_val->intf_id->intf_len = optlen;
1301							relay_val->intf_id->intf_id = (char *)
1302							                              malloc (optlen);
1303
1304							if (relay_val->intf_id->intf_id == NULL) {
1305								dprintf(LOG_ERR, "%s"
1306								                 "failed to allocate memory",
1307							            FNAME);
1308								relayfree(&optinfo->relay_list);
1309								return NULL;
1310							}
1311							else { /* copy the interface identifier so it can
1312						              be sent in the reply */
1313								memcpy (relay_val->intf_id->intf_id,
1314								        ((char *) (option + 1)), optlen);
1315							}
1316						}
1317					}
1318					else {
1319						dprintf(LOG_ERR, "%s" "Invalid length for interface "
1320						                      "identifier option", FNAME);
1321					}
1322				}
1323				else {
1324					dprintf(LOG_INFO, "%s"
1325						"Multiple interface identifier "
1326						"options in RELAY-FORW Message ",
1327					        FNAME);
1328					relayfree(&optinfo->relay_list);
1329					return NULL;
1330				}
1331			}
1332			else if (opt == DH6OPT_RELAY_MSG) {
1333				if (relayed_msg == NULL)
1334					relayed_msg = (struct dhcp6 *) (option + 1);
1335				else {
1336					dprintf(LOG_INFO, "%s" "Duplicated Relay Message option",
1337					        FNAME);
1338					relayfree(&optinfo->relay_list);
1339					return NULL;
1340				}
1341			}
1342			else   /* No other options besides interface identifier and relay
1343			          message make sense, so ignore them with a warning */
1344				dprintf(LOG_INFO, "%s" "Unsupported option %s found in "
1345			                           "RELAY-FORW message",
1346				        FNAME, dhcp6optstr(opt));
1347
1348			/* advance the option pointer */
1349			option = (struct dhcp6opt *) (((char *) (option + 1)) + optlen);
1350		}
1351
1352		/*
1353		 * If the relayed message is non-NULL and is a regular client
1354		 * message, then the relay processing is done. If it is another
1355		 * RELAY_FORW message, then continue. If the relayed message is
1356		 * NULL, signal an error.
1357		 */
1358		if (relayed_msg != NULL && (char *) (relayed_msg + 1) <=
1359		                           (char *) endptr) {
1360			/* done if have found the client message */
1361			if (relayed_msg->dh6_msgtype != DH6_RELAY_FORW)
1362				return relayed_msg;
1363			else
1364				relay_msg = (struct dhcp6_relay *) relayed_msg;
1365		}
1366		else {
1367			dprintf(LOG_ERR, "%s" "invalid relayed message", FNAME);
1368			relayfree(&optinfo->relay_list);
1369			return NULL;
1370		}
1371	}
1372}
1373
1374/*
1375 * Format all of the RELAY-REPL messages and options to send back to the
1376 * client. A RELAY-REPL message and Relay Message option are added for
1377 * each of the relays that were in the RELAY-FORW packet that this is
1378 * in response to.
1379 */
1380static int
1381dhcp6_set_relay (msg, endptr, optinfo)
1382	struct dhcp6_relay *msg;
1383	struct dhcp6_relay *endptr;
1384	struct dhcp6_optinfo *optinfo;
1385{
1386	struct relay_listval *relay;
1387	struct dhcp6opt *option;
1388	int relaylen = 0;
1389	u_int16_t type, len;
1390
1391	for (relay = TAILQ_FIRST(&optinfo->relay_list); relay;
1392	     relay = TAILQ_NEXT(relay, link)) {
1393		/* bounds check */
1394		if (((char *) msg) + sizeof (struct dhcp6_relay) >= (char *) endptr) {
1395			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1396			        FNAME);
1397			return -1;
1398		}
1399
1400		memcpy (msg, &relay->relay, sizeof(struct dhcp6_relay));
1401
1402		relaylen += sizeof(struct dhcp6_relay);
1403
1404		option = (struct dhcp6opt *) (msg + 1);
1405
1406		/* include an Interface Identifier option if it was present in the
1407		   original message */
1408		if (relay->intf_id != NULL) {
1409			/* bounds check */
1410			if ((((char *) option) + sizeof(struct dhcp6opt) +
1411			    relay->intf_id->intf_len) >= (char *) endptr) {
1412				dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1413				        FNAME);
1414				return -1;
1415			}
1416			type = htons(DH6OPT_INTERFACE_ID);
1417			memcpy (&option->dh6opt_type, &type, sizeof(type));
1418			len = htons(relay->intf_id->intf_len);
1419			memcpy (&option->dh6opt_len, &len, sizeof(len));
1420			memcpy (option + 1, relay->intf_id->intf_id,
1421			        relay->intf_id->intf_len);
1422
1423			option = (struct dhcp6opt *)(((char *)(option + 1)) +
1424			                             relay->intf_id->intf_len);
1425			relaylen += sizeof(struct dhcp6opt) + relay->intf_id->intf_len;
1426		}
1427
1428		/* save a pointer to the relay message option so that it is easier
1429		   to fill in the length later */
1430		relay->option = option;
1431
1432		/* bounds check */
1433		if ((char *) (option + 1) >= (char *) endptr) {
1434			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1435			        FNAME);
1436			return -1;
1437		}
1438
1439		/* lastly include the Relay Message option, which encapsulates the
1440		   message being relayed */
1441		type = htons(DH6OPT_RELAY_MSG);
1442		memcpy (&option->dh6opt_type, &type, sizeof(type));
1443		relaylen += sizeof(struct dhcp6opt);
1444		/* dh6opt_len will be set by dhcp6_set_relay_option_len */
1445
1446		msg = (struct dhcp6_relay *) (option + 1);
1447	}
1448
1449	/*
1450	 * if there were no relays, this is an error since this function should
1451	 * not have even been called in this case
1452	 */
1453	if (relaylen == 0)
1454		return -1;
1455	else
1456		return relaylen;
1457}
1458
1459/*
1460 * Fill in all of the opt-len fields for the Relay Message options now that
1461 * the length of the entire message is known.
1462 *
1463 * len - the length of the DHCPv6 message to the client (not including any
1464 *       relay options)
1465 *
1466 * Precondition: dhcp6_set_relay has already been called and the relay->option
1467 *               fields of all of the elements in optinfo->relay_list are
1468 *               non-NULL
1469 */
1470static void
1471dhcp6_set_relay_option_len(optinfo, reply_msg_len)
1472	struct dhcp6_optinfo *optinfo;
1473	int reply_msg_len;
1474{
1475	struct relay_listval *relay, *last = NULL;
1476	u_int16_t len;
1477
1478	for (relay = TAILQ_LAST(&optinfo->relay_list, relay_list);
1479	     relay; relay = TAILQ_PREV(relay, relay_list, link)) {
1480		if (last == NULL) {
1481			len = htons(reply_msg_len);
1482			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
1483			last = relay;
1484		}
1485		else {
1486			len = reply_msg_len + (((void *) (last->option + 1)) -
1487			                        ((void *) (relay->option + 1)));
1488			len = htons(len);
1489			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
1490		}
1491	}
1492}
1493