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 */
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    /* Don't use outsock as it  conflicts with dhcp6c */
343#if 0
344	outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
345	if (outsock < 0) {
346		dprintf(LOG_ERR, "%s" "socket(outsock): %s",
347			FNAME, strerror(errno));
348		exit(1);
349	}
350	if (bind(outsock, res->ai_addr, res->ai_addrlen) < 0) {
351		dprintf(LOG_ERR, "%s" "bind(outsock): %s",
352			FNAME, strerror(errno));
353		exit(1);
354	}
355#endif
356	memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen);
357	sa6_any_downstream =
358		(const struct sockaddr_in6*)&sa6_any_downstream_storage;
359	freeaddrinfo(res);
360
361	/* initialize send/receive buffer */
362	iov.iov_base = (caddr_t)rdatabuf;
363	iov.iov_len = sizeof(rdatabuf);
364	rmh.msg_iov = &iov;
365	rmh.msg_iovlen = 1;
366	rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
367	if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) {
368		dprintf(LOG_ERR, "%s" "memory allocation failed", FNAME);
369		exit(1);
370	}
371	if (num_device != 0) {
372		for (i = 0; i < num_device; i++) {
373			ifidx[i] = if_nametoindex(device[i]);
374			if (ifidx[i] == 0) {
375				dprintf(LOG_ERR, "%s"
376					"invalid interface %s", FNAME, device[0]);
377				exit(1);
378			}
379			ifinit(device[i]);
380		}
381		if (get_duid(DUID_FILE, device[0], &server_duid)) {
382			dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME);
383			exit(1);
384		}
385	} else {
386		/* all the interfaces join multicast group */
387		ifc.ifc_len = sizeof(buff);
388		ifc.ifc_buf = buff;
389		if ((skfd = socket(AF_INET, SOCK_DGRAM,0)) < 0) {
390			dprintf(LOG_ERR, "new socket failed");
391			exit(1);
392		}
393		if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) {
394			dprintf(LOG_ERR, "SIOCGIFCONF: %s\n", strerror(errno));
395			exit(1);
396		}
397		ifr = ifc.ifc_req;
398		for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) {
399			dprintf(LOG_DEBUG, "found device %s", ifr->ifr_name);
400			ifidx[num_device] = if_nametoindex(ifr->ifr_name);
401			if (ifidx[num_device] < 0) {
402				dprintf(LOG_ERR, "%s: unknown interface.\n",
403					ifr->ifr_name);
404				continue;
405			}
406			dprintf(LOG_DEBUG, "if %s index is %d", ifr->ifr_name,
407				ifidx[num_device]);
408#ifdef mshirley
409			if (((ifr->ifr_flags & IFF_UP) == 0)) continue;
410#endif
411			if (strcmp(ifr->ifr_name, "lo")) {
412				/* get our DUID */
413				if (get_duid(DUID_FILE, ifr->ifr_name, &server_duid)) {
414					dprintf(LOG_ERR, "%s" "failed to get a DUID", FNAME);
415					exit(1);
416				}
417			}
418			ifinit(ifr->ifr_name);
419			num_device += 1;
420		}
421	}
422	for (i = 0; i < num_device; i++) {
423		hints.ai_flags = 0;
424		error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2);
425		if (error) {
426			dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
427				FNAME, gai_strerror(error));
428			exit(1);
429		}
430		memset(&mreq6, 0, sizeof(mreq6));
431		mreq6.ipv6mr_interface = ifidx[i];
432		memcpy(&mreq6.ipv6mr_multiaddr,
433	    		&((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
434	    		sizeof(mreq6.ipv6mr_multiaddr));
435		if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
436		    &mreq6, sizeof(mreq6))) {
437			dprintf(LOG_ERR, "%s" "setsockopt(insock, IPV6_JOIN_GROUP) %s",
438				FNAME, strerror(errno));
439			exit(1);
440		}
441		freeaddrinfo(res2);
442
443		hints.ai_flags = 0;
444		error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM,
445				    &hints, &res2);
446		if (error) {
447			dprintf(LOG_ERR, "%s" "getaddrinfo: %s",
448				FNAME, gai_strerror(error));
449			exit(1);
450		}
451		memset(&mreq6, 0, sizeof(mreq6));
452		mreq6.ipv6mr_interface = ifidx[i];
453		memcpy(&mreq6.ipv6mr_multiaddr,
454	    		&((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
455	    		sizeof(mreq6.ipv6mr_multiaddr));
456		if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
457		    &mreq6, sizeof(mreq6))) {
458			dprintf(LOG_ERR,
459				"%s" "setsockopt(insock, IPV6_JOIN_GROUP): %s",
460				FNAME, strerror(errno));
461			exit(1);
462		}
463		freeaddrinfo(res2);
464
465        /* Don't use outsock as it  conflicts with dhcp6c */
466#if 0
467		/* set outgoing interface of multicast packets for DHCP reconfig */
468		if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
469		    &ifidx[i], sizeof(ifidx[i])) < 0) {
470			dprintf(LOG_ERR,
471				"%s" "setsockopt(outsock, IPV6_MULTICAST_IF): %s",
472				FNAME, strerror(errno));
473			exit(1);
474		}
475#endif
476	}
477	/* set up sync lease file timer */
478	sync_lease_timer = dhcp6_add_timer(check_lease_file_timo, NULL);
479	d = DHCP6_SYNCFILE_TIME;
480	timo.tv_sec = (long)d;
481	timo.tv_usec = 0;
482	dprintf(LOG_DEBUG, "set timer for syncing file ...");
483	dhcp6_set_timer(&timo, sync_lease_timer);
484	return;
485}
486
487
488static void
489server6_mainloop()
490{
491	struct timeval *w;
492	int ret;
493	fd_set r;
494
495	while (1) {
496		w = dhcp6_check_timer();
497
498		FD_ZERO(&r);
499		FD_SET(insock, &r);
500		ret = select(insock + 1, &r, NULL, NULL, w);
501		switch (ret) {
502		case -1:
503			dprintf(LOG_ERR, "%s" "select: %s",
504				FNAME, strerror(errno));
505			exit(1);
506			/* NOTREACHED */
507		case 0:		/* timeout */
508			break;
509		default:
510			break;
511		}
512		if (FD_ISSET(insock, &r))
513			server6_recv(insock);
514	}
515}
516
517static int
518server6_recv(s)
519	int s;
520{
521	ssize_t len;
522	struct sockaddr_storage from;
523	int fromlen;
524	struct msghdr mhdr;
525	struct iovec iov;
526	char cmsgbuf[BUFSIZ];
527	struct cmsghdr *cm;
528	struct in6_pktinfo *pi = NULL;
529	struct dhcp6_if *ifp;
530	struct dhcp6 *dh6;
531	struct dhcp6_optinfo optinfo;
532	struct in6_addr relay;  /* the address of the first relay, if any */
533	memset(&iov, 0, sizeof(iov));
534	memset(&mhdr, 0, sizeof(mhdr));
535
536	iov.iov_base = rdatabuf;
537	iov.iov_len = sizeof(rdatabuf);
538	mhdr.msg_name = &from;
539	mhdr.msg_namelen = sizeof(from);
540	mhdr.msg_iov = &iov;
541	mhdr.msg_iovlen = 1;
542	mhdr.msg_control = (caddr_t)cmsgbuf;
543	mhdr.msg_controllen = sizeof(cmsgbuf);
544
545	if ((len = recvmsg(insock, &mhdr, 0)) < 0) {
546		dprintf(LOG_ERR, "%s" "recvmsg: %s", FNAME, strerror(errno));
547		return -1;
548	}
549	fromlen = mhdr.msg_namelen;
550
551	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm;
552	     cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) {
553		if (cm->cmsg_level == IPPROTO_IPV6 &&
554		    cm->cmsg_type == IPV6_PKTINFO &&
555		    cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
556			pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
557		}
558	}
559	if (pi == NULL) {
560		dprintf(LOG_NOTICE, "%s" "failed to get packet info", FNAME);
561		return -1;
562	}
563	dprintf(LOG_DEBUG, "received message packet info addr is %s, scope id (%d)",
564	    in6addr2str(&pi->ipi6_addr, 0), (unsigned int)pi->ipi6_ifindex);
565	if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) {
566		dprintf(LOG_INFO, "%s" "unexpected interface (%d)", FNAME,
567		    (unsigned int)pi->ipi6_ifindex);
568		return -1;
569	}
570	if (len < sizeof(*dh6)) {
571		dprintf(LOG_INFO, "%s" "short packet", FNAME);
572		return -1;
573	}
574
575	dh6 = (struct dhcp6 *)rdatabuf;
576
577	dprintf(LOG_DEBUG, "%s" "received %s from %s", FNAME,
578	    dhcp6msgstr(dh6->dh6_msgtype),
579	    addr2str((struct sockaddr *)&from));
580
581	dhcp6_init_options(&optinfo);
582
583	/*
584	 * If this is a relayed message, parse all of the relay data, storing
585	 * the link addresses, peer addresses, and interface identifiers for
586	 * later use. Get a pointer to the original client message.
587	 */
588	if (dh6->dh6_msgtype == DH6_RELAY_FORW) {
589		dh6 = dhcp6_parse_relay((struct dhcp6_relay *) dh6,
590		                        (struct dhcp6_relay *) (rdatabuf + len),
591		                        &optinfo, &relay);
592
593		/*
594		 * NULL means there was an error in the relay format or no
595		 * client message was found.
596		 */
597		if (dh6 == NULL) {
598			dprintf(LOG_INFO, "%s" "failed to parse relay fields "
599			                       "or could not find client message", FNAME);
600			return -1;
601		}
602	}
603
604	/*
605	 * parse and validate options in the request
606	 */
607    /* Pass extra arg to 'dhcp6_get_options' */
608#if 0
609	if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
610	    (struct dhcp6opt *)(rdatabuf + len), &optinfo) < 0) {
611#endif
612    if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
613        (struct dhcp6opt *)(rdatabuf + len), &optinfo, 0, 0, 0) < 0) {
614		dprintf(LOG_INFO, "%s" "failed to parse options", FNAME);
615		return -1;
616	}
617	/* check host decl first */
618	host = dhcp6_allocate_host(ifp, globalgroup, &optinfo);
619	/* ToDo: allocate subnet after relay agent done
620	 * now assume client is on the same link as server
621	 * if the subnet couldn't be found return status code NotOnLink to client
622	 */
623	/*
624	 * If the relay list is empty, then this is a message received directly
625	 * from the client, so client is on the same link as the server.
626	 * Otherwise, allocate the client an address based on the first relay
627	 * that forwarded the message.
628	 */
629	if (TAILQ_EMPTY(&optinfo.relay_list))
630		subnet = dhcp6_allocate_link(ifp, globalgroup, NULL);
631	else
632		subnet = dhcp6_allocate_link(ifp, globalgroup, &relay);
633
634	if (!(DH6_VALID_MESSAGE(dh6->dh6_msgtype)))
635		dprintf(LOG_INFO, "%s" "unknown or unsupported msgtype %s",
636		    FNAME, dhcp6msgstr(dh6->dh6_msgtype));
637	else
638		server6_react_message(ifp, pi, dh6, &optinfo,
639			(struct sockaddr *)&from, fromlen);
640	dhcp6_clear_options(&optinfo);
641	return 0;
642}
643
644static int
645server6_react_message(ifp, pi, dh6, optinfo, from, fromlen)
646	struct dhcp6_if *ifp;
647	struct in6_pktinfo *pi;
648	struct dhcp6 *dh6;
649	struct dhcp6_optinfo *optinfo;
650	struct sockaddr *from;
651	int fromlen;
652{
653	struct dhcp6_optinfo roptinfo;
654
655	int addr_flag = 0;
656	int addr_request = 0;
657	int resptype = DH6_REPLY;
658	int num = DH6OPT_STCODE_SUCCESS;
659	int sending_hint = 0;
660
661	/* message validation according to Section 18.2 of dhcpv6-28 */
662
663	/* the message must include a Client Identifier option */
664	if (optinfo->clientID.duid_len == 0) {
665		dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
666		return -1;
667	} else {
668		dprintf(LOG_DEBUG, "%s" "client ID %s", FNAME,
669			duidstr(&optinfo->clientID));
670	}
671	/* the message must include a Server Identifier option in below messages*/
672	switch (dh6->dh6_msgtype) {
673	case DH6_REQUEST:
674	case DH6_RENEW:
675        case DH6_DECLINE:
676	case DH6_RELEASE:
677		if (optinfo->serverID.duid_len == 0) {
678			dprintf(LOG_INFO, "%s" "no server ID option", FNAME);
679			return -1;
680		}
681		/* the contents of the Server Identifier option must match ours */
682		if (duidcmp(&optinfo->serverID, &server_duid)) {
683			dprintf(LOG_INFO, "server ID %s mismatch %s",
684				duidstr(&optinfo->serverID), duidstr(&server_duid));
685			return -1;
686		}
687		break;
688	default:
689		break;
690	}
691	/*
692	 * configure necessary options based on the options in request.
693	 */
694	dhcp6_init_options(&roptinfo);
695	/* server information option */
696	if (duidcpy(&roptinfo.serverID, &server_duid)) {
697		dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME);
698		goto fail;
699	}
700	/* copy client information back */
701	if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
702		dprintf(LOG_ERR, "%s" "failed to copy client ID", FNAME);
703		goto fail;
704	}
705	/* if the client is not on the link */
706	if (host == NULL && subnet == NULL) {
707		num = DH6OPT_STCODE_NOTONLINK;
708		/* Draft-28 18.2.2, drop the message if NotOnLink */
709		if (dh6->dh6_msgtype == DH6_CONFIRM || dh6->dh6_msgtype == DH6_REBIND)
710			goto fail;
711		else
712			goto send;
713	}
714	if (subnet) {
715		roptinfo.pref = subnet->linkscope.server_pref;
716		roptinfo.flags = (optinfo->flags & subnet->linkscope.allow_flags) |
717				subnet->linkscope.send_flags;
718		dnslist = subnet->linkscope.dnslist;
719		siplist = subnet->linkscope.siplist;
720		ntplist = subnet->linkscope.ntplist;
721	}
722	if (host) {
723		roptinfo.pref = host->hostscope.server_pref;
724		roptinfo.flags = (optinfo->flags & host->hostscope.allow_flags) |
725				host->hostscope.send_flags;
726		dnslist = host->hostscope.dnslist;
727		siplist = host->hostscope.siplist;
728		ntplist = host->hostscope.ntplist;
729	}
730	/* prohibit a mixture of old and new style of DNS server config */
731	if (!TAILQ_EMPTY(&arg_dnslist.addrlist)) {
732		if (!TAILQ_EMPTY(&dnslist.addrlist)) {
733			dprintf(LOG_INFO, "%s" "do not specify DNS servers "
734			    "both by command line and by configuration file.",
735			    FNAME);
736			exit(1);
737		}
738		dnslist = arg_dnslist;
739		TAILQ_INIT(&arg_dnslist.addrlist);
740	}
741	dprintf(LOG_DEBUG, "server preference is %2x", roptinfo.pref);
742	if (roptinfo.flags & DHCIFF_UNICAST) {
743		/* todo find the right server unicast address to client*/
744		/* get_linklocal(device, &roptinfo.server_addr) */
745		memcpy(&roptinfo.server_addr, &ifp->linklocal,
746		       sizeof(roptinfo.server_addr));
747		dprintf(LOG_DEBUG, "%s" "server address is %s",
748			FNAME, in6addr2str(&roptinfo.server_addr, 0));
749	}
750	/*
751	 * When the server receives a Request message via unicast from a
752	 * client to which the server has not sent a unicast option, the server
753	 * discards the Request message and responds with a Reply message
754	 * containing a Status Code option with value UseMulticast, a Server
755	 * Identifier option containing the server's DUID, the Client
756	 * Identifier option from the client message and no other options.
757	 * [dhcpv6-26 18.2.1]
758	 */
759	switch (dh6->dh6_msgtype) {
760	case DH6_REQUEST:
761	case DH6_RENEW:
762	case DH6_DECLINE:
763		/*
764		 * If the message was relayed, then do not check whether the message
765		 * came in via unicast or multicast, since the relay may be configured
766		 * to send messages via unicast.
767		 */
768		if (TAILQ_EMPTY(&optinfo->relay_list) &&
769		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
770			if (!(roptinfo.flags & DHCIFF_UNICAST)) {
771				num = DH6OPT_STCODE_USEMULTICAST;
772				goto send;
773			} else
774				break;
775		}
776		break;
777	default:
778		/*
779		 * If the message was relayed, then do not check whether the message
780		 * came in via unicast or multicast, since the relay may be configured
781		 * to send messages via unicast.
782		 */
783		if (TAILQ_EMPTY(&optinfo->relay_list) &&
784		    !IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr)) {
785			num = DH6OPT_STCODE_USEMULTICAST;
786			goto send;
787		}
788		break;
789	}
790
791	switch (dh6->dh6_msgtype) {
792	case DH6_SOLICIT:
793		/*
794		 * If the client has included a Rapid Commit option and the
795		 * server has been configured to respond with committed address
796		 * assignments and other resources, responds to the Solicit
797		 * with a Reply message.
798		 * [dhcpv6-28 Section 17.2.1]
799		 * [dhcpv6-28 Section 17.2.2]
800		 * If Solicit has IA option, responds to Solicit with a Advertise
801		 * message.
802		 */
803		if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) {
804			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
805					sizeof(roptinfo.iaidinfo));
806			roptinfo.type = optinfo->type;
807			dprintf(LOG_DEBUG, "option type is %d", roptinfo.type);
808			addr_request = 1;
809			if (roptinfo.flags & DHCIFF_RAPID_COMMIT) {
810				resptype = DH6_REPLY;
811			} else {
812				resptype = DH6_ADVERTISE;
813				/* giving hint ?? */
814				sending_hint = 1;
815			}
816		}
817		break;
818	case DH6_INFORM_REQ:
819		/* don't response to info-req if there is any IA option */
820		if (optinfo->iaidinfo.iaid != 0)
821			goto fail;
822		/* DNS server */
823		if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
824			dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
825			goto fail;
826		}
827        /* Add SIP and NTP server if available */
828		if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) {
829			dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME);
830			goto fail;
831		}
832		if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) {
833			dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME);
834			goto fail;
835		}
836		roptinfo.dns_list.domainlist = dnslist.domainlist;
837		break;
838	case DH6_REQUEST:
839		/* get iaid for that request client for that interface */
840		if (optinfo->iaidinfo.iaid != 0 && !(roptinfo.flags & DHCIFF_INFO_ONLY)) {
841			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
842					sizeof(roptinfo.iaidinfo));
843			roptinfo.type = optinfo->type;
844			addr_request = 1;
845		}
846		break;
847	/*
848	 * Locates the client's binding and verifies that the information
849	 * from the client matches the information stored for that client.
850	 */
851	case DH6_RENEW:
852	case DH6_REBIND:
853	case DH6_DECLINE:
854	case DH6_RELEASE:
855	case DH6_CONFIRM:
856		roptinfo.type = optinfo->type;
857		if (dh6->dh6_msgtype == DH6_RENEW || dh6->dh6_msgtype == DH6_REBIND)
858			addr_flag = ADDR_UPDATE;
859		if (dh6->dh6_msgtype == DH6_RELEASE)
860			addr_flag = ADDR_REMOVE;
861		if (dh6->dh6_msgtype == DH6_CONFIRM) {
862			/* DNS server */
863			addr_flag = ADDR_VALIDATE;
864			if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
865				dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
866				goto fail;
867			}
868			roptinfo.dns_list.domainlist = dnslist.domainlist;
869		}
870		if (dh6->dh6_msgtype == DH6_DECLINE)
871			addr_flag = ADDR_ABANDON;
872	if (optinfo->iaidinfo.iaid != 0) {
873		if (!TAILQ_EMPTY(&optinfo->addr_list) && resptype != DH6_ADVERTISE) {
874			struct dhcp6_iaidaddr *iaidaddr;
875			memcpy(&roptinfo.iaidinfo, &optinfo->iaidinfo,
876					sizeof(roptinfo.iaidinfo));
877			roptinfo.type = optinfo->type;
878			/* find bindings */
879			if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) == NULL) {
880				if (dh6->dh6_msgtype == DH6_REBIND)
881					goto fail;
882				num = DH6OPT_STCODE_NOBINDING;
883				dprintf(LOG_INFO, "%s" "Nobinding for client %s iaid %u",
884					FNAME, duidstr(&optinfo->clientID),
885						optinfo->iaidinfo.iaid);
886				break;
887			}
888			if (addr_flag != ADDR_UPDATE) {
889				dhcp6_copy_list(&roptinfo.addr_list, &optinfo->addr_list);
890			} else {
891				/* get static host configuration */
892				if (host)
893					dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host);
894				/* allow dynamic address assginment for the host too */
895				if (optinfo->type == IAPD)
896					dhcp6_create_prefixlist(&roptinfo,
897								optinfo,
898								iaidaddr,
899								subnet);
900				else
901					dhcp6_create_addrlist(&roptinfo, optinfo,
902							iaidaddr, subnet);
903				/* in case there is not bindings available */
904				if (TAILQ_EMPTY(&roptinfo.addr_list)) {
905					num = DH6OPT_STCODE_NOBINDING;
906					dprintf(LOG_INFO, "%s"
907					    "Bindings are not on link for client %s iaid %u",
908						FNAME, duidstr(&optinfo->clientID),
909						roptinfo.iaidinfo.iaid);
910					break;
911				}
912			}
913			if (addr_flag == ADDR_VALIDATE) {
914				if (dhcp6_validate_bindings(&roptinfo, iaidaddr))
915					num = DH6OPT_STCODE_NOBINDING;
916				break;
917			} else {
918				/* do update if this is not a confirm */
919				if (dhcp6_update_iaidaddr(&roptinfo, addr_flag)
920						!= 0) {
921					dprintf(LOG_INFO, "%s"
922						"bindings failed for client %s iaid %u",
923						FNAME, duidstr(&optinfo->clientID),
924							roptinfo.iaidinfo.iaid);
925					num = DH6OPT_STCODE_UNSPECFAIL;
926					break;
927				}
928			}
929			num = DH6OPT_STCODE_SUCCESS;
930		} else
931			num = DH6OPT_STCODE_NOADDRAVAIL;
932	} else
933		dprintf(LOG_ERR, "invalid message type");
934		break;
935	default:
936		break;
937	}
938	/*
939	 * If the Request message contained an Option Request option, the
940	 * server MUST include options in the Reply message for any options in
941	 * the Option Request option the server is configured to return to the
942	 * client.
943	 * [dhcpv6-26 18.2.1]
944	 * Note: our current implementation always includes all information
945	 * that we can provide.  So we do not have to check the option request
946	 * options.
947	 */
948	if (addr_request == 1) {
949		int found_binding = 0;
950		struct dhcp6_iaidaddr *iaidaddr;
951		/* find bindings */
952		if ((iaidaddr = dhcp6_find_iaidaddr(&roptinfo)) != NULL) {
953			found_binding = 1;
954			addr_flag = ADDR_UPDATE;
955		}
956		if (host)
957			dhcp6_get_hostconf(&roptinfo, optinfo, iaidaddr, host);
958		/* valid and create addresses list */
959		if (optinfo->type == IAPD)
960			dhcp6_create_prefixlist(&roptinfo, optinfo, iaidaddr, subnet);
961		else
962			dhcp6_create_addrlist(&roptinfo, optinfo, iaidaddr, subnet);
963		if (TAILQ_EMPTY(&roptinfo.addr_list)) {
964			num = DH6OPT_STCODE_NOADDRAVAIL;
965		} else if (sending_hint == 0) {
966		/* valid client request address list */
967			if (found_binding) {
968			       if (dhcp6_update_iaidaddr(&roptinfo, addr_flag) != 0) {
969					dprintf(LOG_ERR,
970					"assigned ipv6address for client iaid %u failed",
971						roptinfo.iaidinfo.iaid);
972					num = DH6OPT_STCODE_UNSPECFAIL;
973			       } else
974					num = DH6OPT_STCODE_SUCCESS;
975			} else {
976			       	if (dhcp6_add_iaidaddr(&roptinfo) != 0) {
977					dprintf(LOG_ERR,
978					"assigned ipv6address for client iaid %u failed",
979						roptinfo.iaidinfo.iaid);
980					num = DH6OPT_STCODE_UNSPECFAIL;
981				} else
982					num = DH6OPT_STCODE_SUCCESS;
983			}
984		}
985		/* DNS server */
986		if (dhcp6_copy_list(&roptinfo.dns_list.addrlist, &dnslist.addrlist)) {
987			dprintf(LOG_ERR, "%s" "failed to copy DNS servers", FNAME);
988			goto fail;
989		}
990
991		if (dhcp6_copy_list(&roptinfo.sip_list, &siplist)) {
992			dprintf(LOG_ERR, "%s" "failed to copy SIP servers", FNAME);
993			goto fail;
994		}
995		if (dhcp6_copy_list(&roptinfo.ntp_list, &ntplist)) {
996			dprintf(LOG_ERR, "%s" "failed to copy NTP servers", FNAME);
997			goto fail;
998		}
999
1000		roptinfo.dns_list.domainlist = dnslist.domainlist;
1001	}
1002	/* add address status code */
1003  send:
1004	dprintf(LOG_DEBUG, " status code: %s", dhcp6_stcodestr(num));
1005	if (dhcp6_add_listval(&roptinfo.stcode_list,
1006	   	&num, DHCP6_LISTVAL_NUM) == NULL) {
1007		dprintf(LOG_ERR, "%s" "failed to copy "
1008	    		"status code", FNAME);
1009		goto fail;
1010	}
1011	/* send a reply message. */
1012	(void)server6_send(resptype, ifp, dh6, optinfo, from, fromlen,
1013			   &roptinfo);
1014
1015	dhcp6_clear_options(&roptinfo);
1016	return 0;
1017
1018  fail:
1019	dhcp6_clear_options(&roptinfo);
1020	return -1;
1021}
1022
1023static int
1024server6_send(type, ifp, origmsg, optinfo, from, fromlen, roptinfo)
1025	int type;
1026	struct dhcp6_if *ifp;
1027	struct dhcp6 *origmsg;
1028	struct dhcp6_optinfo *optinfo, *roptinfo;
1029	struct sockaddr *from;
1030	int fromlen;
1031{
1032	char replybuf[BUFSIZ];
1033	struct sockaddr_in6 dst;
1034	int len, optlen, relaylen = 0;
1035	struct dhcp6 *dh6;
1036
1037	if (sizeof(struct dhcp6) > sizeof(replybuf)) {
1038		dprintf(LOG_ERR, "%s" "buffer size assumption failed", FNAME);
1039		return (-1);
1040	}
1041
1042	if (!TAILQ_EMPTY(&optinfo->relay_list) &&
1043	    (relaylen = dhcp6_set_relay((struct dhcp6_relay *) replybuf,
1044	                                (struct dhcp6_relay *) (replybuf +
1045	                                                        sizeof (replybuf)),
1046	                                optinfo)) < 0) {
1047		dprintf(LOG_INFO, "%s" "failed to construct relay message", FNAME);
1048		return (-1);
1049	}
1050
1051	dh6 = (struct dhcp6 *) (replybuf + relaylen);
1052	len = sizeof(*dh6);
1053	memset(dh6, 0, sizeof(*dh6));
1054	dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid;
1055	dh6->dh6_msgtype = (u_int8_t)type;
1056
1057	/* set options in the reply message */
1058	if ((optlen = dhcp6_set_options((struct dhcp6opt *)(dh6 + 1),
1059					(struct dhcp6opt *)(replybuf +
1060							    sizeof(replybuf)),
1061					roptinfo)) < 0) {
1062		dprintf(LOG_INFO, "%s" "failed to construct reply options",
1063			FNAME);
1064		return (-1);
1065	}
1066	len += optlen;
1067
1068	/*
1069	 * If there were any Relay Message options, fill in the option-len
1070	 * field(s) with the appropriate value(s).
1071	 */
1072	if (!TAILQ_EMPTY(&optinfo->relay_list))
1073	    dhcp6_set_relay_option_len(optinfo, len);
1074
1075	len += relaylen;
1076
1077	/* specify the destination and send the reply */
1078	dst = *sa6_any_downstream;
1079	dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr;
1080
1081	/* RELAY-REPL messages need to be directed back to the port the relay
1082	   agent is listening on, namely DH6PORT_UPSTREAM */
1083	if (relaylen > 0)
1084		dst.sin6_port = upstream_port;
1085
1086	dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id;
1087	dprintf(LOG_DEBUG, "send destination address is %s, scope id is %d",
1088		addr2str((struct sockaddr *)&dst), dst.sin6_scope_id);
1089    /* why use 'outsock' to send to client? */
1090	//if (transmit_sa(outsock, &dst, replybuf, len) != 0) {
1091	if (transmit_sa(insock, &dst, replybuf, len) != 0) {
1092		dprintf(LOG_ERR, "%s" "transmit %s to %s failed", FNAME,
1093			dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
1094		return (-1);
1095	}
1096
1097	dprintf(LOG_DEBUG, "%s" "transmit %s to %s", FNAME,
1098		dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
1099
1100	return 0;
1101}
1102
1103static struct dhcp6_timer
1104*check_lease_file_timo(void *arg)
1105{
1106	double d;
1107	struct timeval timo;
1108	struct stat buf;
1109	FILE *file;
1110	stat(PATH_SERVER6_LEASE, &buf);
1111	strcpy(server6_lease_temp, PATH_SERVER6_LEASE);
1112	strcat(server6_lease_temp, "XXXXXX");
1113	if (buf.st_size > MAX_FILE_SIZE) {
1114		file = sync_leases(server6_lease_file, PATH_SERVER6_LEASE, server6_lease_temp);
1115		if (file != NULL)
1116			server6_lease_file = file;
1117	}
1118	d = DHCP6_SYNCFILE_TIME;
1119	timo.tv_sec = (long)d;
1120	timo.tv_usec = 0;
1121	dhcp6_set_timer(&timo, sync_lease_timer);
1122	return sync_lease_timer;
1123}
1124
1125/*
1126 * Parse all of the RELAY-FORW messages and interface ID options. Each
1127 * RELAY-FORW messages will have its hop count, link address, peer-address,
1128 * and interface ID (if any) put into a relay_listval structure.
1129 * A pointer to the actual original client message will be returned.
1130 * If this client message cannot be found, NULL is returned to signal an error.
1131 */
1132static struct dhcp6 *
1133dhcp6_parse_relay(relay_msg, endptr, optinfo, relay_addr)
1134	struct dhcp6_relay *relay_msg;
1135	struct dhcp6_relay *endptr;
1136	struct dhcp6_optinfo *optinfo;
1137	struct in6_addr *relay_addr;
1138{
1139	struct relay_listval *relay_val;
1140	struct dhcp6 *relayed_msg;  /* the original message that the relay
1141	                               received */
1142	struct dhcp6opt *option, *option_endptr = (struct dhcp6opt *) endptr;
1143
1144	u_int16_t optlen;
1145	u_int16_t opt;
1146
1147	while ((relay_msg + 1) < endptr) {
1148		relay_val = (struct relay_listval *)
1149		            calloc (1, sizeof (struct relay_listval));
1150
1151		if (relay_val == NULL) {
1152			dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
1153			relayfree(&optinfo->relay_list);
1154			return NULL;
1155		}
1156
1157		/* copy the msg-type, hop-count, link-address, and peer-address */
1158		memcpy (&relay_val->relay, relay_msg, sizeof (struct dhcp6_relay));
1159
1160		/* set the msg type to relay reply now so that it doesn't need to be
1161		   done when formatting the reply */
1162		relay_val->relay.dh6_msg_type = DH6_RELAY_REPL;
1163
1164		TAILQ_INSERT_TAIL(&optinfo->relay_list, relay_val, link);
1165
1166		/*
1167		 * need to record the first relay's link address field for later use.
1168		 * The first relay is the last one we see, so keep overwriting the
1169		 * relay value.
1170		 */
1171		memcpy (relay_addr, &relay_val->relay.link_addr,
1172		        sizeof (struct in6_addr));
1173
1174		/* now handle the options in the RELAY-FORW message */
1175		/*
1176		 * The only options that should appear in a RELAY-FORW message are:
1177		 * - Interface identifier
1178		 * - Relay message
1179		 *
1180		 * All other options are ignored.
1181		 */
1182		option = (struct dhcp6opt *) (relay_msg + 1);
1183
1184		relayed_msg = NULL; /* if this is NULL at the end of the loop, no
1185		                       relayed message was found */
1186
1187		/* since the order of options is not specified, all of the options
1188		   must be processed */
1189		while ((option + 1) < option_endptr) {
1190			memcpy (&opt, &option->dh6opt_type, sizeof(opt));
1191			opt = ntohs(opt);
1192			memcpy (&optlen, &option->dh6opt_len, sizeof(optlen));
1193			optlen = ntohs(optlen);
1194
1195			if ((char *) (option + 1) + optlen > (char *) option_endptr) {
1196				dprintf(LOG_ERR, "%s" "invalid option length in %s option",
1197				        FNAME, dhcp6optstr(opt));
1198				relayfree(&optinfo->relay_list);
1199				return NULL;
1200			}
1201
1202			if (opt == DH6OPT_INTERFACE_ID) {
1203				/* if this is not the first interface identifier option,
1204				   then the message is incorrectly formed */
1205				if (relay_val->intf_id == NULL) {
1206					if (optlen) {
1207						relay_val->intf_id = (struct intf_id *)
1208						                     malloc (sizeof (struct intf_id));
1209						if (relay_val->intf_id == NULL) {
1210							dprintf(LOG_ERR, "%s" "failed to allocate memory",
1211							        FNAME);
1212							relayfree(&optinfo->relay_list);
1213							return NULL;
1214						}
1215						else {
1216							relay_val->intf_id->intf_len = optlen;
1217							relay_val->intf_id->intf_id = (char *)
1218							                              malloc (optlen);
1219
1220							if (relay_val->intf_id->intf_id == NULL) {
1221								dprintf(LOG_ERR, "%s"
1222								                 "failed to allocate memory",
1223							            FNAME);
1224								relayfree(&optinfo->relay_list);
1225								return NULL;
1226							}
1227							else { /* copy the interface identifier so it can
1228						              be sent in the reply */
1229								memcpy (relay_val->intf_id->intf_id,
1230								        ((char *) (option + 1)), optlen);
1231							}
1232						}
1233					}
1234					else {
1235						dprintf(LOG_ERR, "%s" "Invalid length for interface "
1236						                      "identifier option", FNAME);
1237					}
1238				}
1239				else {
1240					dprintf(LOG_INFO, "%s"
1241						"Multiple interface identifier "
1242						"options in RELAY-FORW Message ",
1243					        FNAME);
1244					relayfree(&optinfo->relay_list);
1245					return NULL;
1246				}
1247			}
1248			else if (opt == DH6OPT_RELAY_MSG) {
1249				if (relayed_msg == NULL)
1250					relayed_msg = (struct dhcp6 *) (option + 1);
1251				else {
1252					dprintf(LOG_INFO, "%s" "Duplicated Relay Message option",
1253					        FNAME);
1254					relayfree(&optinfo->relay_list);
1255					return NULL;
1256				}
1257			}
1258			else   /* No other options besides interface identifier and relay
1259			          message make sense, so ignore them with a warning */
1260				dprintf(LOG_INFO, "%s" "Unsupported option %s found in "
1261			                           "RELAY-FORW message",
1262				        FNAME, dhcp6optstr(opt));
1263
1264			/* advance the option pointer */
1265			option = (struct dhcp6opt *) (((char *) (option + 1)) + optlen);
1266		}
1267
1268		/*
1269		 * If the relayed message is non-NULL and is a regular client
1270		 * message, then the relay processing is done. If it is another
1271		 * RELAY_FORW message, then continue. If the relayed message is
1272		 * NULL, signal an error.
1273		 */
1274		if (relayed_msg != NULL && (char *) (relayed_msg + 1) <=
1275		                           (char *) endptr) {
1276			/* done if have found the client message */
1277			if (relayed_msg->dh6_msgtype != DH6_RELAY_FORW)
1278				return relayed_msg;
1279			else
1280				relay_msg = (struct dhcp6_relay *) relayed_msg;
1281		}
1282		else {
1283			dprintf(LOG_ERR, "%s" "invalid relayed message", FNAME);
1284			relayfree(&optinfo->relay_list);
1285			return NULL;
1286		}
1287	}
1288}
1289
1290/*
1291 * Format all of the RELAY-REPL messages and options to send back to the
1292 * client. A RELAY-REPL message and Relay Message option are added for
1293 * each of the relays that were in the RELAY-FORW packet that this is
1294 * in response to.
1295 */
1296static int
1297dhcp6_set_relay (msg, endptr, optinfo)
1298	struct dhcp6_relay *msg;
1299	struct dhcp6_relay *endptr;
1300	struct dhcp6_optinfo *optinfo;
1301{
1302	struct relay_listval *relay;
1303	struct dhcp6opt *option;
1304	int relaylen = 0;
1305	u_int16_t type, len;
1306
1307	for (relay = TAILQ_FIRST(&optinfo->relay_list); relay;
1308	     relay = TAILQ_NEXT(relay, link)) {
1309		/* bounds check */
1310		if (((char *) msg) + sizeof (struct dhcp6_relay) >= (char *) endptr) {
1311			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1312			        FNAME);
1313			return -1;
1314		}
1315
1316		memcpy (msg, &relay->relay, sizeof(struct dhcp6_relay));
1317
1318		relaylen += sizeof(struct dhcp6_relay);
1319
1320		option = (struct dhcp6opt *) (msg + 1);
1321
1322		/* include an Interface Identifier option if it was present in the
1323		   original message */
1324		if (relay->intf_id != NULL) {
1325			/* bounds check */
1326			if ((((char *) option) + sizeof(struct dhcp6opt) +
1327			    relay->intf_id->intf_len) >= (char *) endptr) {
1328				dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1329				        FNAME);
1330				return -1;
1331			}
1332			type = htons(DH6OPT_INTERFACE_ID);
1333			memcpy (&option->dh6opt_type, &type, sizeof(type));
1334			len = htons(relay->intf_id->intf_len);
1335			memcpy (&option->dh6opt_len, &len, sizeof(len));
1336			memcpy (option + 1, relay->intf_id->intf_id,
1337			        relay->intf_id->intf_len);
1338
1339			option = (struct dhcp6opt *)(((char *)(option + 1)) +
1340			                             relay->intf_id->intf_len);
1341			relaylen += sizeof(struct dhcp6opt) + relay->intf_id->intf_len;
1342		}
1343
1344		/* save a pointer to the relay message option so that it is easier
1345		   to fill in the length later */
1346		relay->option = option;
1347
1348		/* bounds check */
1349		if ((char *) (option + 1) >= (char *) endptr) {
1350			dprintf(LOG_ERR, "%s" "insufficient buffer size for RELAY-REPL",
1351			        FNAME);
1352			return -1;
1353		}
1354
1355		/* lastly include the Relay Message option, which encapsulates the
1356		   message being relayed */
1357		type = htons(DH6OPT_RELAY_MSG);
1358		memcpy (&option->dh6opt_type, &type, sizeof(type));
1359		relaylen += sizeof(struct dhcp6opt);
1360		/* dh6opt_len will be set by dhcp6_set_relay_option_len */
1361
1362		msg = (struct dhcp6_relay *) (option + 1);
1363	}
1364
1365	/*
1366	 * if there were no relays, this is an error since this function should
1367	 * not have even been called in this case
1368	 */
1369	if (relaylen == 0)
1370		return -1;
1371	else
1372		return relaylen;
1373}
1374
1375/*
1376 * Fill in all of the opt-len fields for the Relay Message options now that
1377 * the length of the entire message is known.
1378 *
1379 * len - the length of the DHCPv6 message to the client (not including any
1380 *       relay options)
1381 *
1382 * Precondition: dhcp6_set_relay has already been called and the relay->option
1383 *               fields of all of the elements in optinfo->relay_list are
1384 *               non-NULL
1385 */
1386static void
1387dhcp6_set_relay_option_len(optinfo, reply_msg_len)
1388	struct dhcp6_optinfo *optinfo;
1389	int reply_msg_len;
1390{
1391	struct relay_listval *relay, *last = NULL;
1392	u_int16_t len;
1393
1394	for (relay = TAILQ_LAST(&optinfo->relay_list, relay_list);
1395	     relay; relay = TAILQ_PREV(relay, relay_list, link)) {
1396		if (last == NULL) {
1397			len = htons(reply_msg_len);
1398			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
1399			last = relay;
1400		}
1401		else {
1402			len = reply_msg_len + (((void *) (last->option + 1)) -
1403			                        ((void *) (relay->option + 1)));
1404			len = htons(len);
1405			memcpy (&relay->option->dh6opt_len, &len, sizeof(len));
1406		}
1407	}
1408}
1409