1/*	$Id: lease.c,v 1.1.1.1 2006/12/04 00:45:29 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 <string.h>
36#include <time.h>
37#include <syslog.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <unistd.h>
43#include <fcntl.h>
44#include <sys/socket.h>
45#include <arpa/inet.h>
46#include <netinet/in.h>
47#include <net/if.h>
48#include <linux/sockios.h>
49#include <ifaddrs.h>
50
51#include "queue.h"
52#include "dhcp6.h"
53#include "hash.h"
54#include "config.h"
55#include "common.h"
56#include "lease.h"
57
58extern struct dhcp6_iaidaddr client6_iaidaddr;
59extern FILE *server6_lease_file;
60extern char *server6_lease_temp;
61extern FILE *client6_lease_file;
62extern char *client6_lease_temp;
63u_int32_t do_hash __P((const void *, u_int8_t ));
64static int init_lease_hashes __P((void));
65
66int
67write_lease(const struct dhcp6_lease *lease_ptr,
68	    FILE *file)
69{
70	struct tm brokendown_time;
71	char addr_str[64];
72
73	if ((inet_ntop(AF_INET6, &lease_ptr->lease_addr.addr,
74		addr_str, sizeof(addr_str))) == 0) {
75		dprintf(LOG_DEBUG, "%s" "inet_ntop %s", FNAME, strerror(errno));
76		return (-1);
77	}
78	gmtime_r(&lease_ptr->start_date, &brokendown_time);
79	fprintf(file, "lease %s/%d { \n", addr_str, lease_ptr->lease_addr.plen);
80	fprintf(file, "\t DUID: %s;\n",
81		duidstr(&lease_ptr->iaidaddr->client6_info.clientid));
82	if (dhcp6_mode == DHCP6_MODE_CLIENT)
83		fprintf(file, "\t SDUID: %s;\n",
84			duidstr(&lease_ptr->iaidaddr->client6_info.serverid));
85	fprintf(file, "\t IAID: %u ", lease_ptr->iaidaddr->client6_info.iaidinfo.iaid);
86	fprintf(file, "\t type: %d;\n", lease_ptr->iaidaddr->client6_info.type);
87	fprintf(file, "\t RenewTime: %u;\n",
88		lease_ptr->iaidaddr->client6_info.iaidinfo.renewtime);
89	fprintf(file, "\t RebindTime: %u;\n",
90		lease_ptr->iaidaddr->client6_info.iaidinfo.rebindtime);
91	if (!IN6_IS_ADDR_UNSPECIFIED(&lease_ptr->linklocal)) {
92		if ((inet_ntop(AF_INET6, &lease_ptr->linklocal, addr_str,
93			sizeof(struct in6_addr))) == 0) {
94			dprintf(LOG_DEBUG, "%s" "inet_ntop %s", FNAME, strerror(errno));
95			return (-1);
96		}
97		fprintf(file, "\t linklocal: %s;\n", addr_str);
98	}
99	fprintf(file, "\t state: %d;\n", lease_ptr->state);
100	if (lease_ptr->hostname != NULL)
101		fprintf(file, "\t hostname: %s;\n",lease_ptr->hostname);
102	fprintf(file, "\t (start_date: %d %d/%d/%d %d:%d:%d UTC);\n",
103		     brokendown_time.tm_wday,
104		     brokendown_time.tm_year + 1900,
105		     brokendown_time.tm_mon + 1,
106		     brokendown_time.tm_mday,
107		     brokendown_time.tm_hour,
108		     brokendown_time.tm_min,
109		     brokendown_time.tm_sec);
110	fprintf(file, "\t start date: %lu;\n", lease_ptr->start_date);
111	fprintf(file, "\t PreferredLifeTime: %u;\n",
112                             lease_ptr->lease_addr.preferlifetime);
113	fprintf(file, "\t ValidLifeTime: %u;\n",
114                             lease_ptr->lease_addr.validlifetime);
115	fprintf(file, "}\n");
116	if (fflush(file) == EOF) {
117		dprintf(LOG_INFO, "%s" "write lease fflush failed %s",
118			FNAME, strerror(errno));
119		return -1;
120	}
121	if (fsync(fileno(file)) < 0) {
122		dprintf(LOG_INFO, "%s" "write lease fsync failed %s",
123			FNAME, strerror(errno));
124		return -1;
125	}
126	return 0;
127}
128
129FILE *
130sync_leases (FILE *file, const char *original, char *template)
131{
132	int i, fd;
133	struct hashlist_element *element;
134	fd = mkstemp(template);
135        if (fd < 0 || (sync_file = fdopen(fd, "w")) == NULL) {
136		dprintf(LOG_ERR, "%s" "could not open sync file", FNAME);
137                return (NULL);
138        }
139	if (dhcp6_mode == DHCP6_MODE_SERVER) {
140		for (i = 0; i < lease_hash_table->hash_size; i++) {
141			element = lease_hash_table->hash_list[i];
142			while (element) {
143				if (write_lease((struct dhcp6_lease *)element->data,
144							sync_file) < 0) {
145					dprintf(LOG_ERR, "%s" "write lease failed", FNAME);
146					return (NULL);
147				}
148				element = element->next;
149			}
150		}
151	} else if (dhcp6_mode == DHCP6_MODE_CLIENT) {
152		struct dhcp6_lease *lv, *lv_next;
153		for (lv = TAILQ_FIRST(&client6_iaidaddr.lease_list); lv; lv = lv_next) {
154			lv_next = TAILQ_NEXT(lv, link);
155			if (write_lease(lv, sync_file) < 0)
156				dprintf(LOG_ERR, "%s" "write lease failed", FNAME);
157		}
158	}
159	fclose(sync_file);
160	fclose(file);
161	if (rename(template, original) < 0) {
162		dprintf(LOG_ERR, "%s" "Could not rename sync file", FNAME);
163		return (NULL);
164	}
165        if ((file = fopen(original, "a+")) == NULL) {
166                dprintf(LOG_ERR, "%s" "could not open sync file", FNAME);
167		return (NULL);
168	}
169	return file;
170}
171
172struct dhcp6_timer *
173syncfile_timo(void *arg)
174{
175	return NULL;
176}
177
178FILE *
179init_leases(const char *name)
180{
181	FILE *file;
182	file = fopen(name, "a+");
183	if(!file) {
184		dprintf(LOG_ERR, "%s" "could not open lease file", FNAME);
185		return (NULL);
186	}
187	if (dhcp6_mode == DHCP6_MODE_SERVER) {
188		if (0 != init_lease_hashes()) {
189			dprintf(LOG_ERR, "%s" "Could not initialize hash arrays", FNAME);
190			return (NULL);
191		}
192	}
193	lease_parse(file);
194	return file;
195}
196
197int
198init_lease_hashes(void)
199{
200
201	hash_anchors = (struct hash_table **)malloc(HASH_TABLE_COUNT*sizeof(*hash_anchors));
202	if (!hash_anchors) {
203		dprintf(LOG_ERR, "%s" "Couldn't malloc hash anchors", FNAME);
204		return (-1);
205	}
206        host_addr_hash_table = hash_table_create(DEFAULT_HASH_SIZE,
207			addr_hash, v6addr_findkey, v6addr_key_compare);
208	if (!host_addr_hash_table) {
209		dprintf(LOG_ERR, "%s" "Couldn't create hash table", FNAME);
210		return (-1);
211	}
212        lease_hash_table = hash_table_create(DEFAULT_HASH_SIZE,
213			addr_hash, lease_findkey, lease_key_compare);
214	if (!lease_hash_table) {
215		dprintf(LOG_ERR, "%s" "Couldn't create hash table", FNAME);
216		return (-1);
217	}
218        server6_hash_table = hash_table_create(DEFAULT_HASH_SIZE,
219			iaid_hash, iaid_findkey, iaid_key_compare);
220	if (!server6_hash_table) {
221		dprintf(LOG_ERR, "%s" "Couldn't create hash table", FNAME);
222		return (-1);
223	}
224	return 0;
225
226}
227
228u_int32_t
229do_hash(const void *key, u_int8_t len)
230{
231	int i;
232	u_int32_t *p;
233	u_int32_t index = 0;
234	u_int32_t tempkey;
235	for (i = 0, p = (u_int32_t *)key; i < len/sizeof(tempkey); i++, p++ ) {
236		memcpy(&tempkey, p, sizeof(tempkey));
237		index ^= tempkey;
238	}
239	memcpy(&tempkey, p, len%(sizeof(tempkey)));
240	index ^= tempkey;
241	return index;
242}
243
244unsigned int
245iaid_hash(const void *key)
246{
247	const struct client6_if *iaidkey = (const struct client6_if *)key;
248	const struct duid *duid = &iaidkey->clientid;
249	unsigned int index;
250	index = do_hash((const void *) duid->duid_id, duid->duid_len);
251	return index;
252}
253
254unsigned int
255addr_hash(const void *key)
256{
257	const struct in6_addr *addrkey
258		= (const struct in6_addr *)&(((const struct dhcp6_addr *)key)->addr);
259	unsigned int index;
260	index = do_hash((const void *)addrkey, sizeof(*addrkey));
261	return index;
262}
263
264void *
265v6addr_findkey(const void *data)
266{
267        const struct dhcp6_addr *v6addr = (const struct dhcp6_addr *)data;
268	return (void *)(&(v6addr->addr));
269}
270
271int
272v6addr_key_compare(const void *data, const void *key)
273{
274	struct dhcp6_addr *v6addr = (struct dhcp6_addr *)data;
275	if (IN6_ARE_ADDR_EQUAL(&v6addr->addr, (struct in6_addr *)key)) {
276		return MATCH;
277	} else
278		return MISCOMPARE;
279}
280
281void *
282lease_findkey(const void *data)
283{
284        const struct dhcp6_lease *lease = (const struct dhcp6_lease *)data;
285	return (void *)(&(lease->lease_addr));
286}
287
288int
289lease_key_compare(const void *data, const void *key)
290{
291	struct dhcp6_lease *lease = (struct dhcp6_lease *)data;
292	struct dhcp6_addr *lease_address = &lease->lease_addr;
293	struct dhcp6_addr *addr6 = (struct dhcp6_addr *)key;
294	if (IN6_ARE_ADDR_EQUAL(&lease_address->addr, &addr6->addr)) {
295		/* prefix match */
296		if (addr6->type == IAPD) {
297		 	if (lease_address->plen == addr6->plen)
298	       			return MATCH;
299		/* ipv6 address match */
300		} else if (addr6->type == IANA || addr6->type == IATA)
301			return MATCH;
302	}
303	return MISCOMPARE;
304}
305
306void *
307iaid_findkey(const void *data)
308{
309        struct dhcp6_iaidaddr *iaidaddr = (struct dhcp6_iaidaddr *)data;
310	return (void *)(&(iaidaddr->client6_info));
311}
312
313int
314iaid_key_compare(const void *data,
315		 const void *key)
316{
317        const struct dhcp6_iaidaddr *iaidaddr = (const struct dhcp6_iaidaddr *)data;
318	const struct client6_if *client_key = (const struct client6_if *)key;
319
320	if (0 == duidcmp(&client_key->clientid, &iaidaddr->client6_info.clientid)){
321		if ((client_key->type == iaidaddr->client6_info.type) &&
322		    (client_key->iaidinfo.iaid == iaidaddr->client6_info.iaidinfo.iaid)) {
323			return MATCH;
324		}
325	}
326	return MISCOMPARE;
327}
328
329int
330prefixcmp(addr, prefix, len)
331	struct in6_addr *addr;
332	struct in6_addr *prefix;
333	int len;
334{
335	int i, num_bytes;
336	struct in6_addr mask;
337	num_bytes = len / 8;
338	for (i = 0; i < num_bytes; i++) {
339		mask.s6_addr[i] = 0xFF;
340	}
341	mask.s6_addr[num_bytes] = 0xFF << (8 - len % 8);
342	for (i = 0; i < num_bytes; i++) {
343		if (addr->s6_addr[i] != prefix->s6_addr[i]) return -1;
344	}
345	if((addr->s6_addr[num_bytes] & mask.s6_addr[num_bytes]) !=
346	   (prefix->s6_addr[num_bytes] & mask.s6_addr[num_bytes]))
347 		return -1;
348	return 0;
349}
350
351int
352get_linklocal(const char *ifname,
353	      struct in6_addr *linklocal)
354{
355	struct ifaddrs *ifa, *ifap;
356	struct sockaddr *sd;
357	if (getifaddrs(&ifap) < 0) {
358		dprintf(LOG_ERR, "getifaddrs error");
359		return -1;
360	}
361	/* ifa->ifa_addr is sockaddr_in6 */
362	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
363		if (strcmp(ifa->ifa_name, ifname)) continue;
364		sd = (struct sockaddr *)ifa->ifa_addr;
365		if (!sd || sd->sa_family != AF_INET6) continue;
366		if (!IN6_IS_ADDR_LINKLOCAL(&sd->sa_data[6])) continue;
367		/* which linklocal do we want, if find many
368		 * from scope id??? sa_data[32]
369		 * */
370		memcpy(linklocal, &sd->sa_data[6], sizeof(*linklocal));
371		break;
372	}
373	freeifaddrs(ifap);
374	return 0;
375}
376
377int
378dhcp6_get_prefixlen(addr, ifp)
379	struct in6_addr *addr;
380	struct dhcp6_if *ifp;
381{
382	struct ra_info *rainfo;
383	for (rainfo = ifp->ralist; rainfo; rainfo = rainfo->next) {
384		/* prefixes are sorted by plen */
385		if (prefixcmp(addr, &rainfo->prefix, rainfo->plen) == 0)
386			return rainfo->plen;
387	}
388	return PREFIX_LEN_NOTINRA;
389}
390
391int
392addr_on_addrlist(addrlist, addr6)
393	struct dhcp6_list *addrlist;
394	struct dhcp6_addr *addr6;
395{
396	struct dhcp6_listval *lv;
397
398	for (lv = TAILQ_FIRST(addrlist); lv;
399	     lv = TAILQ_NEXT(lv, link)) {
400		if (IN6_ARE_ADDR_EQUAL(&lv->val_dhcp6addr.addr, &addr6->addr)) {
401			if ((lv->val_dhcp6addr.type != IAPD)
402			    || ((lv->val_dhcp6addr.type == IAPD)
403			    && (lv->val_dhcp6addr.plen == addr6->plen)))
404				return (1);
405		}
406	}
407	return (0);
408}
409
410u_int32_t
411get_min_preferlifetime(struct dhcp6_iaidaddr *sp)
412{
413	struct dhcp6_lease *lv, *first;
414	u_int32_t min;
415	if (TAILQ_EMPTY(&sp->lease_list))
416		return 0;
417	first = TAILQ_FIRST(&sp->lease_list);
418	min = first->lease_addr.preferlifetime;
419	for (lv = TAILQ_FIRST(&sp->lease_list); lv; lv = TAILQ_NEXT(lv, link)) {
420		min = MIN(min, lv->lease_addr.preferlifetime);
421	}
422	return min;
423}
424
425u_int32_t
426get_max_validlifetime(struct dhcp6_iaidaddr *sp)
427{
428	struct dhcp6_lease *lv, *first;
429	u_int32_t max;
430	if (TAILQ_EMPTY(&sp->lease_list))
431		return 0;
432	first = TAILQ_FIRST(&sp->lease_list);
433	max = first->lease_addr.validlifetime;
434	for (lv = TAILQ_FIRST(&sp->lease_list); lv; lv = TAILQ_NEXT(lv, link)) {
435		max = MAX(max, lv->lease_addr.validlifetime);
436	}
437	return max;
438}
439