1/*	$Id: server6_addr.c,v 1.1.1.1 2006/12/04 00:45:33 Exp $	*/
2
3/*
4 * Copyright (C) International Business Machines  Corp., 2003
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/* Author: Shirley Ma, xma@us.ibm.com */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <time.h>
37//#include <openssl/md5.h>
38
39#include <sys/types.h>
40#include <sys/time.h>
41#include <sys/socket.h>
42#include <sys/ioctl.h>
43
44
45#include <net/if.h>
46#include <netinet/in.h>
47
48#include <errno.h>
49#include <syslog.h>
50#include <string.h>
51#include <unistd.h>
52
53#include "queue.h"
54#include "dhcp6.h"
55#include "config.h"
56#include "common.h"
57#include "server6_conf.h"
58#include "lease.h"
59#include "timer.h"
60#include "hash.h"
61
62extern FILE *server6_lease_file;
63
64struct dhcp6_lease *
65dhcp6_find_lease __P((struct dhcp6_iaidaddr *, struct dhcp6_addr *));
66static int dhcp6_add_lease __P((struct dhcp6_iaidaddr *, struct dhcp6_addr *));
67static int dhcp6_update_lease __P((struct dhcp6_addr *, struct dhcp6_lease *));
68static int addr_on_segment __P((struct v6addrseg *, struct dhcp6_addr *));
69static void  server6_get_newaddr __P((iatype_t, struct dhcp6_addr *, struct v6addrseg *));
70static void  server6_get_addrpara __P((struct dhcp6_addr *, struct v6addrseg *));
71static void  server6_get_prefixpara __P((struct dhcp6_addr *, struct v6prefix *));
72
73struct link_decl *dhcp6_allocate_link __P((struct dhcp6_if *, struct rootgroup *,
74			struct in6_addr *));
75struct host_decl *dhcp6_allocate_host __P((struct dhcp6_if *, struct rootgroup *,
76			struct dhcp6_optinfo *));
77int dhcp6_get_hostconf __P((struct dhcp6_optinfo *, struct dhcp6_optinfo *,
78			struct dhcp6_iaidaddr *, struct host_decl *));
79
80struct host_decl
81*find_hostdecl(duid, iaid, hostlist)
82	struct duid *duid;
83	u_int32_t iaid;
84	struct host_decl *hostlist;
85{
86	struct host_decl *host;
87	for (host = hostlist; host; host = host->next) {
88		if (!duidcmp(duid, &host->cid) && host->iaidinfo.iaid == iaid)
89			return host;
90		continue;
91	}
92
93	return NULL;
94}
95
96/* for request/solicit rapid commit */
97int
98dhcp6_add_iaidaddr(optinfo)
99	struct dhcp6_optinfo *optinfo;
100{
101	struct dhcp6_iaidaddr *iaidaddr;
102	struct dhcp6_listval *lv, *lv_next = NULL;
103	struct timeval timo;
104	double d;
105
106	iaidaddr = (struct dhcp6_iaidaddr *)malloc(sizeof(*iaidaddr));
107	if (iaidaddr == NULL) {
108		dprintf(LOG_ERR, "%s" "failed to allocate memory", FNAME);
109		return (-1);
110	}
111	memset(iaidaddr, 0, sizeof(*iaidaddr));
112	duidcpy(&iaidaddr->client6_info.clientid, &optinfo->clientID);
113	iaidaddr->client6_info.iaidinfo.iaid = optinfo->iaidinfo.iaid;
114	iaidaddr->client6_info.type = optinfo->type;
115	TAILQ_INIT(&iaidaddr->lease_list);
116	/* add new leases */
117	for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
118		lv_next = TAILQ_NEXT(lv, link);
119		if ((hash_search(lease_hash_table, (void *)&lv->val_dhcp6addr)) != NULL) {
120			dprintf(LOG_INFO, "%s" "address for %s has been used",
121				FNAME, in6addr2str(&lv->val_dhcp6addr.addr, 0));
122			TAILQ_REMOVE(&optinfo->addr_list, lv, link);
123			continue;
124		}
125		if (dhcp6_add_lease(iaidaddr, &lv->val_dhcp6addr) != 0)
126			TAILQ_REMOVE(&optinfo->addr_list, lv, link);
127	}
128	/* it's meaningless to have an iaid without any leases */
129	if (TAILQ_EMPTY(&iaidaddr->lease_list)) {
130		dprintf(LOG_INFO, "%s" "no leases are added for duid %s iaid %u",
131			FNAME, duidstr(&iaidaddr->client6_info.clientid),
132				iaidaddr->client6_info.iaidinfo.iaid);
133		return (0);
134	}
135	if (hash_add(server6_hash_table, &iaidaddr->client6_info, iaidaddr)) {
136		dprintf(LOG_ERR, "%s" "failed to hash_add an iaidaddr %u for client duid %s",
137			FNAME, iaidaddr->client6_info.iaidinfo.iaid,
138				duidstr(&iaidaddr->client6_info.clientid));
139		dhcp6_remove_iaidaddr(iaidaddr);
140		return (-1);
141	}
142	dprintf(LOG_DEBUG, "%s" "hash_add an iaidaddr %u for client duid %s",
143		FNAME, iaidaddr->client6_info.iaidinfo.iaid,
144			duidstr(&iaidaddr->client6_info.clientid));
145	/* set up timer for iaidaddr */
146	if ((iaidaddr->timer =
147	    dhcp6_add_timer(dhcp6_iaidaddr_timo, iaidaddr)) == NULL) {
148		dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
149			FNAME, iaidaddr->client6_info.iaidinfo.iaid);
150		dhcp6_remove_iaidaddr(iaidaddr);
151		return (-1);
152	}
153	time(&iaidaddr->start_date);
154	iaidaddr->state = ACTIVE;
155	d = get_max_validlifetime(iaidaddr);
156	timo.tv_sec = (long)d;
157	timo.tv_usec = 0;
158	dhcp6_set_timer(&timo, iaidaddr->timer);
159	return (0);
160}
161
162int
163dhcp6_remove_iaidaddr(iaidaddr)
164	struct dhcp6_iaidaddr *iaidaddr;
165{
166	struct dhcp6_lease *lv, *lv_next;
167	struct dhcp6_lease *lease;
168
169	/* remove all the leases in this iaid */
170	for (lv = TAILQ_FIRST(&iaidaddr->lease_list); lv; lv = lv_next) {
171		lv_next = TAILQ_NEXT(lv, link);
172		if ((lease = hash_search(lease_hash_table, (void *)&lv->lease_addr)) != NULL) {
173			if (dhcp6_remove_lease(lv)) {
174				dprintf(LOG_ERR, "%s" "failed to remove an iaid %u", FNAME,
175					 iaidaddr->client6_info.iaidinfo.iaid);
176				return (-1);
177			}
178		}
179	}
180	if (hash_delete(server6_hash_table, &iaidaddr->client6_info) != 0) {
181		dprintf(LOG_ERR, "%s" "failed to remove an iaid %u from hash",
182			FNAME, iaidaddr->client6_info.iaidinfo.iaid);
183		return (-1);
184	}
185	if (iaidaddr->timer)
186		dhcp6_remove_timer(iaidaddr->timer);
187	dprintf(LOG_DEBUG, "%s" "removed iaidaddr %u", FNAME,
188		iaidaddr->client6_info.iaidinfo.iaid);
189	free(iaidaddr);
190	return (0);
191}
192
193struct dhcp6_iaidaddr
194*dhcp6_find_iaidaddr(optinfo)
195	struct dhcp6_optinfo *optinfo;
196{
197	struct dhcp6_iaidaddr *iaidaddr;
198	struct client6_if client6_info;
199	duidcpy(&client6_info.clientid, &optinfo->clientID);
200	client6_info.iaidinfo.iaid = optinfo->iaidinfo.iaid;
201	client6_info.type = optinfo->type;
202	if ((iaidaddr = hash_search(server6_hash_table, (void *)&client6_info)) == NULL) {
203		dprintf(LOG_DEBUG, "%s" "iaid %u iaidaddr for client duid %s doesn't exists",
204			FNAME, client6_info.iaidinfo.iaid,
205			duidstr(&client6_info.clientid));
206	}
207	duidfree(&client6_info.clientid);
208	return iaidaddr;
209}
210
211int
212dhcp6_remove_lease(lease)
213	struct dhcp6_lease *lease;
214{
215	lease->state = INVALID;
216	if (write_lease(lease, server6_lease_file) != 0) {
217		dprintf(LOG_ERR, "%s" "failed to write an invalid lease %s to lease file",
218			FNAME, in6addr2str(&lease->lease_addr.addr, 0));
219		return (-1);
220	}
221	if (hash_delete(lease_hash_table, &lease->lease_addr) != 0) {
222		dprintf(LOG_ERR, "%s" "failed to remove an address %s from hash",
223			FNAME, in6addr2str(&lease->lease_addr.addr, 0));
224		return (-1);
225	}
226	if (lease->timer)
227		dhcp6_remove_timer(lease->timer);
228	TAILQ_REMOVE(&lease->iaidaddr->lease_list, lease, link);
229	dprintf(LOG_DEBUG, "%s" "removed lease %s", FNAME,
230		in6addr2str(&lease->lease_addr.addr, 0));
231	free(lease);
232	return 0;
233}
234
235/* for renew/rebind/release/decline */
236int
237dhcp6_update_iaidaddr(optinfo, flag)
238	struct dhcp6_optinfo *optinfo;
239	int flag;
240{
241	struct dhcp6_iaidaddr *iaidaddr;
242	struct dhcp6_lease *lease, *lease_next = NULL;
243	struct dhcp6_listval *lv, *lv_next = NULL;
244	struct timeval timo;
245	double d;
246
247	if ((iaidaddr = dhcp6_find_iaidaddr(optinfo)) == NULL) {
248		return (-1);
249	}
250
251	if (flag == ADDR_UPDATE) {
252		/* add or update new lease */
253		for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
254			lv_next = TAILQ_NEXT(lv, link);
255			dprintf(LOG_DEBUG, "%s" "address is %s " , FNAME,
256					in6addr2str(&lv->val_dhcp6addr.addr,0));
257			if ((lease = dhcp6_find_lease(iaidaddr, &lv->val_dhcp6addr))
258					!= NULL) {
259				dhcp6_update_lease(&lv->val_dhcp6addr, lease);
260			} else {
261				dhcp6_add_lease(iaidaddr, &lv->val_dhcp6addr);
262			}
263		}
264		/* remove leases that not on the reply list */
265		for (lease = TAILQ_FIRST(&iaidaddr->lease_list); lease; lease = lease_next) {
266			lease_next = TAILQ_NEXT(lease, link);
267			if (!addr_on_addrlist(&optinfo->addr_list, &lease->lease_addr)) {
268				dprintf(LOG_DEBUG, "%s" "lease %s is not on the link",
269						FNAME, in6addr2str(&lease->lease_addr.addr,0));
270				dhcp6_remove_lease(lease);
271			}
272		}
273		dprintf(LOG_DEBUG, "%s" "update iaidaddr for iaid %u", FNAME,
274			iaidaddr->client6_info.iaidinfo.iaid);
275	} else {
276		/* remove leases */
277		for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) {
278			lv_next = TAILQ_NEXT(lv, link);
279			lease = dhcp6_find_lease(iaidaddr, &lv->val_dhcp6addr);
280			if (lease) {
281				if (flag == ADDR_ABANDON) {
282				}
283				dhcp6_remove_lease(lease);
284			} else {
285				dprintf(LOG_INFO, "%s" "address is not on the iaid", FNAME);
286			}
287		}
288
289	}
290	/* it's meaningless to have an iaid without any leases */
291	if (TAILQ_EMPTY(&iaidaddr->lease_list)) {
292		dprintf(LOG_INFO, "%s" "no leases are added for duid %s iaid %u",
293			FNAME, duidstr(&iaidaddr->client6_info.clientid),
294				iaidaddr->client6_info.iaidinfo.iaid);
295		dhcp6_remove_iaidaddr(iaidaddr);
296		return (0);
297	}
298	/* update the start date and timer */
299	if (iaidaddr->timer == NULL) {
300		if ((iaidaddr->timer =
301		     dhcp6_add_timer(dhcp6_iaidaddr_timo, iaidaddr)) == NULL) {
302	 		dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u",
303				FNAME, iaidaddr->client6_info.iaidinfo.iaid);
304	 		return (-1);
305	    	}
306	}
307	time(&iaidaddr->start_date);
308	iaidaddr->state = ACTIVE;
309	d = get_max_validlifetime(iaidaddr);
310	timo.tv_sec = (long)d;
311	timo.tv_usec = 0;
312	dhcp6_set_timer(&timo, iaidaddr->timer);
313	return (0);
314}
315
316int
317dhcp6_validate_bindings(optinfo, iaidaddr)
318	struct dhcp6_optinfo *optinfo;
319	struct dhcp6_iaidaddr *iaidaddr;
320{
321	struct dhcp6_listval *lv;
322
323	for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = TAILQ_NEXT(lv, link)) {
324		if (dhcp6_find_lease(iaidaddr, &lv->val_dhcp6addr) == NULL)
325			return (-1);
326	}
327	return 0;
328}
329
330int
331dhcp6_add_lease(iaidaddr, addr)
332	struct dhcp6_iaidaddr *iaidaddr;
333	struct dhcp6_addr *addr;
334{
335	struct dhcp6_lease *sp;
336	struct timeval timo;
337	double d;
338	if (addr->status_code != DH6OPT_STCODE_SUCCESS &&
339			addr->status_code != DH6OPT_STCODE_UNDEFINE) {
340		dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME,
341			in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code));
342		return (0);
343	}
344	/* ignore meaningless address, this never happens */
345	if (addr->validlifetime == 0 || addr->preferlifetime == 0) {
346		dprintf(LOG_INFO, "%s" "zero address life time for %s",
347			FNAME, in6addr2str(&addr->addr, 0));
348		return (0);
349	}
350
351	if (((sp = hash_search(lease_hash_table, (void *)addr))) != NULL) {
352		dprintf(LOG_INFO, "%s" "duplicated address: %s",
353		    FNAME, in6addr2str(&addr->addr, 0));
354		return (-1);
355	}
356
357	if ((sp = (struct dhcp6_lease *)malloc(sizeof(*sp))) == NULL) {
358		dprintf(LOG_ERR, "%s" "failed to allocate memory"
359			" for an address", FNAME);
360		return (-1);
361	}
362	memset(sp, 0, sizeof(*sp));
363	memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr));
364	sp->iaidaddr = iaidaddr;
365	/* ToDo: preferlifetime EXPIRED; validlifetime DELETED; */
366	/* if a finite lease perferlifetime is specified, set up a timer. */
367	time(&sp->start_date);
368	dprintf(LOG_DEBUG, "%s" "start date is %ld", FNAME, sp->start_date);
369	sp->state = ACTIVE;
370	if (write_lease(sp, server6_lease_file) != 0) {
371		dprintf(LOG_ERR, "%s" "failed to write a new lease address %s to lease file",
372			FNAME, in6addr2str(&sp->lease_addr.addr, 0));
373		free(sp->timer);
374		free(sp);
375		return (-1);
376	}
377	dprintf(LOG_DEBUG, "%s" "write lease %s/%d to lease file", FNAME,
378		in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen);
379	if (hash_add(lease_hash_table, &sp->lease_addr, sp)) {
380		dprintf(LOG_ERR, "%s" "failed to add hash for an address", FNAME);
381			free(sp->timer);
382			free(sp);
383			return (-1);
384	}
385	TAILQ_INSERT_TAIL(&iaidaddr->lease_list, sp, link);
386	if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE ||
387	    sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) {
388		dprintf(LOG_INFO, "%s" "infinity address life time for %s",
389			FNAME, in6addr2str(&addr->addr, 0));
390		return (0);
391	}
392	if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) {
393		dprintf(LOG_ERR, "%s" "failed to create a new event "
394	    		"timer", FNAME);
395		free(sp);
396		return (-1);
397	}
398	d = sp->lease_addr.preferlifetime;
399	timo.tv_sec = (long)d;
400	timo.tv_usec = 0;
401	dhcp6_set_timer(&timo, sp->timer);
402	dprintf(LOG_DEBUG, "%s" "add lease for %s/%d iaid %u with preferlifetime %u"
403			" with validlifetime %u", FNAME,
404		in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen,
405		sp->iaidaddr->client6_info.iaidinfo.iaid,
406		sp->lease_addr.preferlifetime, sp->lease_addr.validlifetime);
407	return (0);
408}
409
410/* assume we've found the updated lease already */
411int
412dhcp6_update_lease(addr, sp)
413	struct dhcp6_addr *addr;
414	struct dhcp6_lease *sp;
415{
416	struct timeval timo;
417	double d;
418	if (addr->status_code != DH6OPT_STCODE_SUCCESS &&
419			addr->status_code != DH6OPT_STCODE_UNDEFINE) {
420		dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME,
421			in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code));
422		dhcp6_remove_lease(sp);
423		return (0);
424	}
425	/* remove lease with perferlifetime or validlifetime 0 */
426	if (addr->validlifetime == 0 || addr->preferlifetime == 0) {
427		dprintf(LOG_INFO, "%s" "zero address life time for %s",
428			FNAME, in6addr2str(&addr->addr, 0));
429		dhcp6_remove_lease(sp);
430		return (0);
431	}
432	memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr));
433	time(&sp->start_date);
434	sp->state = ACTIVE;
435	if (write_lease(sp, server6_lease_file) != 0) {
436		dprintf(LOG_ERR, "%s" "failed to write an updated lease %s to lease file",
437			FNAME, in6addr2str(&sp->lease_addr.addr, 0));
438		return (-1);
439	}
440	if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE ||
441	    sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) {
442		dprintf(LOG_INFO, "%s" "infinity address life time for %s",
443			FNAME, in6addr2str(&addr->addr, 0));
444		return (0);
445	}
446	if (sp->timer == NULL) {
447		if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) {
448			dprintf(LOG_ERR, "%s" "failed to create a new event "
449	    			"timer", FNAME);
450			return (-1);
451		}
452	}
453	d = sp->lease_addr.preferlifetime;
454	timo.tv_sec = (long)d;
455	timo.tv_usec = 0;
456	dhcp6_set_timer(&timo, sp->timer);
457	return (0);
458}
459
460struct dhcp6_lease *
461dhcp6_find_lease(iaidaddr, ifaddr)
462	struct dhcp6_iaidaddr *iaidaddr;
463	struct dhcp6_addr *ifaddr;
464{
465	struct dhcp6_lease *sp;
466	for (sp = TAILQ_FIRST(&iaidaddr->lease_list); sp;
467	     sp = TAILQ_NEXT(sp, link)) {
468		/* check for prefix length
469		 * sp->lease_addr.plen == ifaddr->plen &&
470		 */
471      		dprintf(LOG_DEBUG, "%s" "request address is %s/%d ", FNAME,
472			in6addr2str(&ifaddr->addr, 0), ifaddr->plen);
473      		dprintf(LOG_DEBUG, "%s" "lease address is %s/%d ", FNAME,
474			in6addr2str(&sp->lease_addr.addr, 0), ifaddr->plen);
475	      	if (IN6_ARE_ADDR_EQUAL(&sp->lease_addr.addr, &ifaddr->addr)) {
476			if (ifaddr->type == IAPD) {
477				if (sp->lease_addr.plen == ifaddr->plen)
478					return (sp);
479			} else if (ifaddr->type == IANA || ifaddr->type == IATA)
480				return (sp);
481		}
482	}
483	return (NULL);
484}
485
486struct dhcp6_timer *
487dhcp6_iaidaddr_timo(void *arg)
488{
489	struct dhcp6_iaidaddr *sp = (struct dhcp6_iaidaddr *)arg;
490
491	dprintf(LOG_DEBUG, "server6_iaidaddr timeout for %u, state=%d",
492		sp->client6_info.iaidinfo.iaid, sp->state);
493	switch(sp->state) {
494	case ACTIVE:
495	case EXPIRED:
496		sp->state = EXPIRED;
497		dhcp6_remove_iaidaddr(sp);
498	default:
499		break;
500	}
501	return (NULL);
502}
503
504struct dhcp6_timer *
505dhcp6_lease_timo(arg)
506	void *arg;
507{
508	struct dhcp6_lease *sp = (struct dhcp6_lease *)arg;
509	struct timeval timeo;
510	double d;
511
512	dprintf(LOG_DEBUG, "%s" "lease timeout for %s, state=%d", FNAME,
513		in6addr2str(&sp->lease_addr.addr, 0), sp->state);
514
515	switch(sp->state) {
516	case ACTIVE:
517		sp->state = EXPIRED;
518		d = sp->lease_addr.validlifetime - sp->lease_addr.preferlifetime;
519		timeo.tv_sec = (long)d;
520		timeo.tv_usec = 0;
521		dhcp6_set_timer(&timeo, sp->timer);
522		break;
523	case EXPIRED:
524	case INVALID:
525		sp->state = INVALID;
526		dhcp6_remove_lease(sp);
527		return (NULL);
528	default:
529		return (NULL);
530	}
531	return (sp->timer);
532}
533
534static void
535get_random_bytes(u_int8_t seed[], int num)
536{
537	int i;
538	for (i = 0; i < num; i++)
539		seed[i] = random();
540	return;
541}
542
543
544static void
545create_tempaddr(prefix, plen, tempaddr)
546	struct in6_addr *prefix;
547	int plen;
548	struct in6_addr *tempaddr;
549{
550	int i, num_bytes;
551	u_int8_t seed[16];
552
553	get_random_bytes(seed, 16);
554	/* address mask */
555	memset(tempaddr, 0, sizeof(*tempaddr));
556	num_bytes = plen / 8;
557	for (i = 0; i < num_bytes; i++) {
558		tempaddr->s6_addr[i] = prefix->s6_addr[i];
559	}
560	tempaddr->s6_addr[num_bytes] = (prefix->s6_addr[num_bytes] | (0xFF >> plen % 8))
561		& (seed[num_bytes] | ((0xFF << 8) - plen % 8));
562
563	for (i = num_bytes + 1; i < 16; i++) {
564		tempaddr->s6_addr[i] = seed[i];
565	}
566	return;
567}
568
569int
570dhcp6_get_hostconf(roptinfo, optinfo, iaidaddr, host)
571	struct host_decl *host;
572	struct dhcp6_iaidaddr *iaidaddr;
573	struct dhcp6_optinfo *optinfo, *roptinfo;
574{
575	struct dhcp6_list *reply_list = &roptinfo->addr_list;
576
577	if (!(host->hostscope.allow_flags & DHCIFF_TEMP_ADDRS)) {
578		roptinfo->iaidinfo.renewtime = host->hostscope.renew_time;
579		roptinfo->iaidinfo.rebindtime = host->hostscope.rebind_time;
580		roptinfo->type = optinfo->type;
581		switch (optinfo->type) {
582		case IANA:
583			dhcp6_copy_list(reply_list, &host->addrlist);
584			break;
585		case IATA:
586			break;
587		case IAPD:
588			dhcp6_copy_list(reply_list, &host->prefixlist);
589			break;
590		}
591	}
592	return 0;
593}
594
595int
596dhcp6_create_addrlist(roptinfo, optinfo, iaidaddr, subnet)
597	struct dhcp6_optinfo *roptinfo;
598	struct dhcp6_optinfo *optinfo;
599	const struct dhcp6_iaidaddr *iaidaddr;
600	const struct link_decl *subnet;
601{
602	struct dhcp6_listval *v6addr;
603	struct v6addrseg *seg;
604	struct dhcp6_list *reply_list = &roptinfo->addr_list;
605	struct dhcp6_list *req_list = &optinfo->addr_list;
606	int numaddr;
607	struct dhcp6_listval *lv, *lv_next = NULL;
608
609	roptinfo->iaidinfo.renewtime = subnet->linkscope.renew_time;
610	roptinfo->iaidinfo.rebindtime = subnet->linkscope.rebind_time;
611	roptinfo->type = optinfo->type;
612	/* check the duplication */
613	for (lv = TAILQ_FIRST(req_list); lv; lv = lv_next) {
614		lv_next = TAILQ_NEXT(lv, link);
615		if (addr_on_addrlist(reply_list, &lv->val_dhcp6addr)) {
616			TAILQ_REMOVE(req_list, lv, link);
617		}
618	}
619	dhcp6_copy_list(reply_list, req_list);
620	for (lv = TAILQ_FIRST(reply_list); lv; lv = lv_next) {
621			lv_next = TAILQ_NEXT(lv, link);
622		lv->val_dhcp6addr.type = optinfo->type;
623		lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE;
624		lv->val_dhcp6addr.status_msg = NULL;
625	}
626	for (seg = subnet->seglist; seg; seg = seg->next) {
627		numaddr = 0;
628		for (lv = TAILQ_FIRST(reply_list); lv; lv = lv_next) {
629			lv_next = TAILQ_NEXT(lv, link);
630			/* skip checked segment */
631			if (lv->val_dhcp6addr.status_code == DH6OPT_STCODE_SUCCESS)
632				continue;
633			if (IN6_IS_ADDR_RESERVED(&lv->val_dhcp6addr.addr) ||
634			    is_anycast(&lv->val_dhcp6addr.addr, seg->prefix.plen)) {
635				lv->val_dhcp6addr.status_code = DH6OPT_STCODE_NOTONLINK;
636				dprintf(LOG_DEBUG, "%s" " %s address not on link", FNAME,
637					in6addr2str(&lv->val_dhcp6addr.addr, 0));
638				continue;
639			}
640			lv->val_dhcp6addr.type = optinfo->type;
641			if (addr_on_segment(seg, &lv->val_dhcp6addr)) {
642				if (numaddr == 0) {
643					lv->val_dhcp6addr.type = optinfo->type;
644					server6_get_addrpara(&lv->val_dhcp6addr, seg);
645					numaddr += 1;
646				} else {
647			/* check the addr count per seg, we only allow one address
648			 * per segment, set the status code */
649					lv->val_dhcp6addr.status_code
650							= DH6OPT_STCODE_NOADDRAVAIL;
651				}
652			} else {
653				lv->val_dhcp6addr.status_code = DH6OPT_STCODE_NOTONLINK;
654				dprintf(LOG_DEBUG, "%s" " %s address not on link", FNAME,
655					in6addr2str(&lv->val_dhcp6addr.addr, 0));
656			}
657		}
658		if (iaidaddr != NULL) {
659			struct dhcp6_lease *cl;
660			for (cl = TAILQ_FIRST(&iaidaddr->lease_list); cl;
661					cl = TAILQ_NEXT(cl, link)) {
662				if (addr_on_segment(seg, &cl->lease_addr)) {
663					if (addr_on_addrlist(reply_list,
664								&cl->lease_addr)) {
665						continue;
666					} else if (numaddr == 0) {
667						v6addr = (struct dhcp6_listval *)malloc(sizeof(*v6addr));
668						if (v6addr == NULL) {
669							dprintf(LOG_ERR, "%s"
670								"fail to allocate memory %s",
671								FNAME, strerror(errno));
672							return (-1);
673						}
674						memset(v6addr, 0, sizeof(*v6addr));
675						memcpy(&v6addr->val_dhcp6addr, &cl->lease_addr,
676							sizeof(v6addr->val_dhcp6addr));
677						v6addr->val_dhcp6addr.type = optinfo->type;
678						server6_get_addrpara(&v6addr->val_dhcp6addr,
679									seg);
680						numaddr += 1;
681						TAILQ_INSERT_TAIL(reply_list, v6addr, link);
682						continue;
683					}
684				}
685
686			}
687		}
688		if (numaddr == 0) {
689			v6addr = (struct dhcp6_listval *)malloc(sizeof(*v6addr));
690			if (v6addr == NULL) {
691				dprintf(LOG_ERR, "%s" "fail to allocate memory %s",
692					FNAME, strerror(errno));
693				return (-1);
694			}
695			memset(v6addr, 0, sizeof(*v6addr));
696			v6addr->val_dhcp6addr.type = optinfo->type;
697			server6_get_newaddr(optinfo->type, &v6addr->val_dhcp6addr, seg);
698			if (IN6_IS_ADDR_UNSPECIFIED(&v6addr->val_dhcp6addr.addr)) {
699				free(v6addr);
700				continue;
701			}
702			TAILQ_INSERT_TAIL(reply_list, v6addr, link);
703		}
704	}
705	return (0);
706}
707
708static int
709addr_on_segment(seg, addr)
710	struct v6addrseg *seg;
711	struct dhcp6_addr *addr;
712{
713	int onseg = 0;
714	struct v6addr *prefix;
715	dprintf(LOG_DEBUG, "%s" " checking address %s on segment",
716		FNAME, in6addr2str(&addr->addr, 0));
717	switch (addr->type) {
718	case IATA:
719		prefix = getprefix(&addr->addr, seg->prefix.plen);
720		if (prefix && !memcmp(&seg->prefix, prefix, sizeof(seg->prefix))) {
721			dprintf(LOG_DEBUG, "%s" " address is on link", FNAME);
722			onseg = 1;
723		} else
724			onseg = 0;
725		free(prefix);
726		break;
727	case IANA:
728		if (ipv6addrcmp(&addr->addr, &seg->min) >= 0 &&
729		    ipv6addrcmp(&seg->max, &addr->addr) >= 0) {
730			dprintf(LOG_DEBUG, "%s" " address is on link", FNAME);
731			onseg = 1;
732		}
733		else
734			onseg = 0;
735		break;
736	default:
737		break;
738	}
739	return onseg;
740
741}
742
743static void
744server6_get_newaddr(type, v6addr, seg)
745	iatype_t type;
746	struct dhcp6_addr *v6addr;
747	struct v6addrseg *seg;
748{
749	struct in6_addr current;
750	int round = 0;
751	memcpy(&current, &seg->free, sizeof(current));
752	do {
753		v6addr->type = type;
754		switch(type) {
755		case IATA:
756			/* assume the temp addr never being run out */
757			create_tempaddr(&seg->prefix.addr, seg->prefix.plen, &v6addr->addr);
758			break;
759		case IANA:
760			memcpy(&v6addr->addr, &seg->free, sizeof(v6addr->addr));
761			if (round && IN6_ARE_ADDR_EQUAL(&current, &v6addr->addr)) {
762				memset(&v6addr->addr, 0, sizeof(v6addr->addr));
763				break;
764			}
765			inc_ipv6addr(&seg->free);
766			if (ipv6addrcmp(&seg->free, &seg->max) == 1 ) {
767				round = 1;
768				memcpy(&seg->free, &seg->min, sizeof(seg->free));
769			}
770			break;
771		default:
772			break;
773		}
774
775	} while ((hash_search(lease_hash_table, (void *)v6addr) != NULL) ||
776		 (hash_search(host_addr_hash_table, (void *)&v6addr->addr) != NULL) ||
777		 (is_anycast(&v6addr->addr, seg->prefix.plen)));
778	if (IN6_IS_ADDR_UNSPECIFIED(&v6addr->addr)) {
779		return;
780	}
781	dprintf(LOG_DEBUG, "new address %s is got", in6addr2str(&v6addr->addr, 0));
782	server6_get_addrpara(v6addr, seg);
783	return;
784}
785
786static void
787server6_get_prefixpara(v6addr, seg)
788	struct dhcp6_addr *v6addr;
789	struct v6prefix *seg;
790{
791	v6addr->plen = seg->prefix.plen;
792	if (seg->parainfo.prefer_life_time == 0 && seg->parainfo.valid_life_time == 0) {
793		seg->parainfo.valid_life_time = DEFAULT_VALID_LIFE_TIME;
794		seg->parainfo.prefer_life_time = DEFAULT_PREFERRED_LIFE_TIME;
795	} else if (seg->parainfo.prefer_life_time == 0) {
796		seg->parainfo.prefer_life_time = seg->parainfo.valid_life_time / 2;
797	} else if (seg->parainfo.valid_life_time == 0) {
798		seg->parainfo.valid_life_time = 2 * seg->parainfo.prefer_life_time;
799	}
800	dprintf(LOG_DEBUG, " preferlifetime %u, validlifetime %u",
801		seg->parainfo.prefer_life_time, seg->parainfo.valid_life_time);
802
803	dprintf(LOG_DEBUG, " renewtime %u, rebindtime %u",
804		seg->parainfo.renew_time, seg->parainfo.rebind_time);
805	v6addr->preferlifetime = seg->parainfo.prefer_life_time;
806	v6addr->validlifetime = seg->parainfo.valid_life_time;
807	v6addr->status_code = DH6OPT_STCODE_SUCCESS;
808	v6addr->status_msg = NULL;
809	return;
810}
811
812static void
813server6_get_addrpara(v6addr, seg)
814	struct dhcp6_addr *v6addr;
815	struct v6addrseg *seg;
816{
817	v6addr->plen = seg->prefix.plen;
818	if (seg->parainfo.prefer_life_time == 0 && seg->parainfo.valid_life_time == 0) {
819		seg->parainfo.valid_life_time = DEFAULT_VALID_LIFE_TIME;
820		seg->parainfo.prefer_life_time = DEFAULT_PREFERRED_LIFE_TIME;
821	} else if (seg->parainfo.prefer_life_time == 0) {
822		seg->parainfo.prefer_life_time = seg->parainfo.valid_life_time / 2;
823	} else if (seg->parainfo.valid_life_time == 0) {
824		seg->parainfo.valid_life_time = 2 * seg->parainfo.prefer_life_time;
825	}
826	dprintf(LOG_DEBUG, " preferlifetime %u, validlifetime %u",
827		seg->parainfo.prefer_life_time, seg->parainfo.valid_life_time);
828
829	dprintf(LOG_DEBUG, " renewtime %u, rebindtime %u",
830		seg->parainfo.renew_time, seg->parainfo.rebind_time);
831	v6addr->preferlifetime = seg->parainfo.prefer_life_time;
832	v6addr->validlifetime = seg->parainfo.valid_life_time;
833	v6addr->status_code = DH6OPT_STCODE_SUCCESS;
834	v6addr->status_msg = NULL;
835	return;
836}
837
838int
839dhcp6_create_prefixlist(roptinfo, optinfo, iaidaddr, subnet)
840	struct dhcp6_optinfo *roptinfo;
841	const struct dhcp6_optinfo *optinfo;
842	const struct dhcp6_iaidaddr *iaidaddr;
843	const struct link_decl *subnet;
844{
845	struct dhcp6_listval *v6addr;
846	struct v6prefix *prefix6;
847	struct dhcp6_list *reply_list = &roptinfo->addr_list;
848	const struct dhcp6_list *req_list = &optinfo->addr_list;
849	struct dhcp6_listval *lv, *lv_next = NULL;
850
851	roptinfo->iaidinfo.renewtime = subnet->linkscope.renew_time;
852	roptinfo->iaidinfo.rebindtime = subnet->linkscope.rebind_time;
853	roptinfo->type = optinfo->type;
854	for (prefix6 = subnet->prefixlist; prefix6; prefix6 = prefix6->next) {
855		v6addr = (struct dhcp6_listval *)malloc(sizeof(*v6addr));
856		if (v6addr == NULL) {
857			dprintf(LOG_ERR, "%s" "fail to allocate memory", FNAME);
858			return (-1);
859		}
860		memset(v6addr, 0, sizeof(*v6addr));
861		memcpy(&v6addr->val_dhcp6addr.addr, &prefix6->prefix.addr,
862				sizeof(v6addr->val_dhcp6addr.addr));
863		v6addr->val_dhcp6addr.plen = prefix6->prefix.plen;
864		v6addr->val_dhcp6addr.type = IAPD;
865		server6_get_prefixpara(&v6addr->val_dhcp6addr, prefix6);
866		dprintf(LOG_DEBUG, " get prefix %s/%d, "
867			"preferlifetime %u, validlifetime %u",
868			in6addr2str(&v6addr->val_dhcp6addr.addr, 0),
869			v6addr->val_dhcp6addr.plen,
870			v6addr->val_dhcp6addr.preferlifetime,
871			v6addr->val_dhcp6addr.validlifetime);
872		TAILQ_INSERT_TAIL(reply_list, v6addr, link);
873	}
874	for (prefix6 = subnet->prefixlist; prefix6; prefix6 = prefix6->next) {
875		for (lv = TAILQ_FIRST(req_list); lv; lv = lv_next) {
876			lv_next = TAILQ_NEXT(lv, link);
877			if (IN6_IS_ADDR_RESERVED(&lv->val_dhcp6addr.addr) ||
878			    is_anycast(&lv->val_dhcp6addr.addr, prefix6->prefix.plen) ||
879			    !addr_on_addrlist(reply_list, &lv->val_dhcp6addr)) {
880				lv->val_dhcp6addr.status_code = DH6OPT_STCODE_NOTONLINK;
881				dprintf(LOG_DEBUG, " %s prefix not on link",
882					in6addr2str(&lv->val_dhcp6addr.addr, 0));
883				lv->val_dhcp6addr.type = IAPD;
884				TAILQ_INSERT_TAIL(reply_list, lv, link);
885			}
886		}
887	}
888	return (0);
889}
890
891struct host_decl *
892dhcp6_allocate_host(ifp, rootgroup, optinfo)
893	struct dhcp6_if *ifp;
894	struct rootgroup *rootgroup;
895	struct dhcp6_optinfo *optinfo;
896{
897	struct host_decl *host = NULL;
898	struct interface *ifnetwork;
899	struct duid *duid = &optinfo->clientID;
900	u_int32_t iaid = optinfo->iaidinfo.iaid;
901	for (ifnetwork = rootgroup->iflist; ifnetwork; ifnetwork = ifnetwork->next) {
902		if (strcmp(ifnetwork->name, ifp->ifname) != 0)
903			continue;
904		else {
905			host = find_hostdecl(duid, iaid, ifnetwork->hostlist);
906			break;
907		}
908	}
909	return host;
910}
911
912struct link_decl *
913dhcp6_allocate_link(ifp, rootgroup, relay)
914	struct dhcp6_if *ifp;
915	struct rootgroup *rootgroup;
916	struct in6_addr *relay;
917{
918	struct link_decl *link;
919	struct interface *ifnetwork;
920	ifnetwork = rootgroup->iflist;
921	for (ifnetwork = rootgroup->iflist; ifnetwork; ifnetwork = ifnetwork->next) {
922		if (strcmp(ifnetwork->name, ifp->ifname) != 0)
923			continue;
924		else {
925			for (link = ifnetwork->linklist; link; link = link->next) {
926				/* if relay is NULL, assume client and server are on the
927				 * same link (which cannot have a relay configuration option)
928				 */
929				struct v6addrlist *temp;
930				if (relay == NULL) {
931					if (link->relaylist != NULL)
932						continue;
933					else
934						return link;
935				} else {
936					for (temp = link->relaylist; temp; temp = temp->next) {
937						/* only compare the prefix configured to the relay
938						   link address */
939						if (!prefixcmp (relay,
940									    &temp->v6addr.addr,
941						                temp->v6addr.plen))
942							return link;
943						else
944							continue;
945					}
946				}
947			}
948		}
949	}
950	return NULL;
951}
952