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