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(¤t, &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(¤t, &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