1/* $Id: client6_addr.c,v 1.1.1.1 2006/12/04 00:45:21 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 <sys/types.h> 35#include <sys/time.h> 36#include <sys/socket.h> 37#include <sys/ioctl.h> 38 39#include <linux/ipv6.h> 40 41#include <net/if.h> 42#include <time.h> 43#include <errno.h> 44#include <syslog.h> 45#include <string.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <unistd.h> 49#include <net/if_arp.h> 50 51#include "queue.h" 52#include "dhcp6.h" 53#include "config.h" 54#include "common.h" 55#include "timer.h" 56#include "lease.h" 57 58static int dhcp6_update_lease __P((struct dhcp6_addr *, struct dhcp6_lease *)); 59static int dhcp6_add_lease __P((struct dhcp6_addr *)); 60struct dhcp6_lease *dhcp6_find_lease __P((struct dhcp6_iaidaddr *, 61 struct dhcp6_addr *)); 62int dhcp6_get_prefixlen __P((struct in6_addr *, struct dhcp6_if *)); 63int client6_ifaddrconf __P((ifaddrconf_cmd_t, struct dhcp6_addr *)); 64u_int32_t get_min_preferlifetime __P((struct dhcp6_iaidaddr *)); 65u_int32_t get_max_validlifetime __P((struct dhcp6_iaidaddr *)); 66struct dhcp6_timer *dhcp6_iaidaddr_timo __P((void *)); 67struct dhcp6_timer *dhcp6_lease_timo __P((void *)); 68 69extern struct dhcp6_iaidaddr client6_iaidaddr; 70extern struct dhcp6_timer *client6_timo __P((void *)); 71extern void client6_send __P((struct dhcp6_event *)); 72extern void free_servers __P((struct dhcp6_if *)); 73extern ssize_t gethwid __P((char *, int, const char *, u_int16_t *)); 74 75extern int nlsock; 76extern FILE *client6_lease_file; 77extern struct dhcp6_iaidaddr client6_iaidaddr; 78extern struct dhcp6_list request_list; 79 80extern char* get_dhcpc_dev_name(void); 81 82void 83dhcp6_init_iaidaddr(void) 84{ 85 memset(&client6_iaidaddr, 0, sizeof(client6_iaidaddr)); 86 TAILQ_INIT(&client6_iaidaddr.lease_list); 87} 88 89static char callback_cmd[256] = ""; 90int dhcp6c_dad_callback(void) 91{ 92 /* Execute our callback function to restart other 93 * user-space apps, such as radvd, dhcp6s, etc 94 */ 95 if (strlen(callback_cmd)) 96 { 97 system(callback_cmd); 98 memset(callback_cmd, 0, sizeof(callback_cmd)); 99 } 100 return 0; 101} 102 103int 104dhcp6_add_iaidaddr(struct dhcp6_optinfo *optinfo) 105{ 106 struct dhcp6_listval *lv, *lv_next = NULL; 107 struct timeval timo; 108 struct dhcp6_lease *cl_lease; 109 double d; 110 111 char command[256], command2[256]; 112 memset(command, 0, sizeof(command)); 113 114 /* ignore IA with T1 > T2 */ 115 if (client6_iaidaddr.client6_info.iaidinfo.renewtime > 116 client6_iaidaddr.client6_info.iaidinfo.rebindtime) { 117 dprintf(LOG_INFO, " T1 time is greater than T2 time"); 118 return (0); 119 } 120 memcpy(&client6_iaidaddr.client6_info.iaidinfo, &optinfo->iaidinfo, 121 sizeof(client6_iaidaddr.client6_info.iaidinfo)); 122 client6_iaidaddr.client6_info.type = optinfo->type; 123 duidcpy(&client6_iaidaddr.client6_info.clientid, &optinfo->clientID); 124 if (duidcpy(&client6_iaidaddr.client6_info.serverid, &optinfo->serverID)) { 125 dprintf(LOG_ERR, "%s" "failed to copy server ID %s", 126 FNAME, duidstr(&optinfo->serverID)); 127 return (-1); 128 } 129 /* add new address */ 130 for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) { 131 lv_next = TAILQ_NEXT(lv, link); 132 if (lv->val_dhcp6addr.type != IAPD) { 133 lv->val_dhcp6addr.plen = 134 dhcp6_get_prefixlen(&lv->val_dhcp6addr.addr, dhcp6_if); 135 if (lv->val_dhcp6addr.plen == PREFIX_LEN_NOTINRA) { 136 dprintf(LOG_WARNING, 137 "assigned address %s prefix len is not in any RAs" 138 " prefix length using 64 bit instead", 139 in6addr2str(&lv->val_dhcp6addr.addr, 0)); 140 141 sprintf(command, "dhcp6c_up %s %s %d ", 142 get_dhcpc_dev_name(), 143 in6addr2str(&lv->val_dhcp6addr.addr, 0), 144 lv->val_dhcp6addr.plen); 145 } 146 } 147 if ((cl_lease = dhcp6_find_lease(&client6_iaidaddr, 148 &lv->val_dhcp6addr)) != NULL) { 149 dhcp6_update_lease(&lv->val_dhcp6addr, cl_lease); 150 continue; 151 } 152 if (dhcp6_add_lease(&lv->val_dhcp6addr)) { 153 dprintf(LOG_ERR, "%s" "failed to add a new addr lease %s", 154 FNAME, in6addr2str(&lv->val_dhcp6addr.addr, 0)); 155 continue; 156 } 157 } 158 if (TAILQ_EMPTY(&client6_iaidaddr.lease_list)) 159 return 0; 160 161 /* add new prefix (IAPD) */ 162 for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; lv = lv_next) { 163 lv_next = TAILQ_NEXT(lv, link); 164 if (lv->val_dhcp6addr.type == IAPD) { 165 sprintf(command2, " %s %d &", 166 in6addr2str(&lv->val_dhcp6addr.addr, 0), 167 lv->val_dhcp6addr.plen); 168 if (!strlen(command)) 169 sprintf(command, "dhcp6c_up %s ", get_dhcpc_dev_name()); 170 strcat(command, command2); 171 } 172 } 173 174 /* set up renew T1, rebind T2 timer renew/rebind based on iaid */ 175 /* Should we process IA_TA, IA_NA differently */ 176 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0 || 177 client6_iaidaddr.client6_info.iaidinfo.renewtime > 178 client6_iaidaddr.client6_info.iaidinfo.rebindtime) { 179 u_int32_t min_plifetime; 180 min_plifetime = get_min_preferlifetime(&client6_iaidaddr); 181 if (min_plifetime == DHCP6_DURATITION_INFINITE) 182 client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime; 183 else 184 client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime / 2; 185 } 186 if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0 || 187 client6_iaidaddr.client6_info.iaidinfo.renewtime > 188 client6_iaidaddr.client6_info.iaidinfo.rebindtime) { 189 client6_iaidaddr.client6_info.iaidinfo.rebindtime = 190 get_min_preferlifetime(&client6_iaidaddr) * 4 / 5; 191 } 192 dprintf(LOG_INFO, "renew time %d, rebind time %d", 193 client6_iaidaddr.client6_info.iaidinfo.renewtime, 194 client6_iaidaddr.client6_info.iaidinfo.rebindtime); 195 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) 196 return (0); 197 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == DHCP6_DURATITION_INFINITE) { 198 client6_iaidaddr.client6_info.iaidinfo.rebindtime = DHCP6_DURATITION_INFINITE; 199 return (0); 200 } 201 /* set up start date, and renew timer */ 202 if ((client6_iaidaddr.timer = 203 dhcp6_add_timer(dhcp6_iaidaddr_timo, &client6_iaidaddr)) == NULL) { 204 dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u", 205 FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid); 206 return (-1); 207 } 208 time(&client6_iaidaddr.start_date); 209 client6_iaidaddr.state = ACTIVE; 210 d = client6_iaidaddr.client6_info.iaidinfo.renewtime; 211 timo.tv_sec = (long)d; 212 timo.tv_usec = 0; 213 dhcp6_set_timer(&timo, client6_iaidaddr.timer); 214 215 /* Call our callback function to do something useful */ 216 if (strlen(command)) 217 strcpy(callback_cmd, command); 218 //system(command); 219 220 return (0); 221} 222 223int 224dhcp6_add_lease(addr) 225 struct dhcp6_addr *addr; 226{ 227 struct dhcp6_lease *sp; 228 struct timeval timo; 229 double d; 230 231 dprintf(LOG_DEBUG, "%s" "try to add address %s", FNAME, 232 in6addr2str(&addr->addr, 0)); 233 234 /* ignore meaningless address */ 235 if (addr->status_code != DH6OPT_STCODE_SUCCESS && 236 addr->status_code != DH6OPT_STCODE_UNDEFINE) { 237 dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME, 238 in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code)); 239 return (0); 240 } 241 if (addr->validlifetime == 0 || addr->preferlifetime == 0 || 242 addr->preferlifetime > addr->validlifetime) { 243 dprintf(LOG_ERR, "%s" "invalid address life time for %s", 244 FNAME, in6addr2str(&addr->addr, 0)); 245 return (0); 246 } 247 if ((sp = dhcp6_find_lease(&client6_iaidaddr, addr)) != NULL) { 248 dprintf(LOG_ERR, "%s" "duplicated address: %s", 249 FNAME, in6addr2str(&addr->addr, 0)); 250 return (-1); 251 } 252 if ((sp = (struct dhcp6_lease *)malloc(sizeof(*sp))) == NULL) { 253 dprintf(LOG_ERR, "%s" "failed to allocate memory" 254 " for a addr", FNAME); 255 return (-1); 256 } 257 memset(sp, 0, sizeof(*sp)); 258 memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr)); 259 sp->iaidaddr = &client6_iaidaddr; 260 time(&sp->start_date); 261 sp->state = ACTIVE; 262 if (write_lease(sp, client6_lease_file) != 0) { 263 dprintf(LOG_ERR, "%s" "failed to write a new lease address %s to lease file", 264 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 265 if (sp->timer) 266 dhcp6_remove_timer(sp->timer); 267 free(sp); 268 return (-1); 269 } 270 if (sp->lease_addr.type == IAPD) { 271 dprintf(LOG_INFO, "request prefix is %s/%d", 272 in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen); 273 } else if (client6_ifaddrconf(IFADDRCONF_ADD, addr) != 0) { 274 dprintf(LOG_ERR, "%s" "adding address failed: %s", 275 FNAME, in6addr2str(&addr->addr, 0)); 276 if (sp->timer) 277 dhcp6_remove_timer(sp->timer); 278 free(sp); 279 return (-1); 280 } 281 TAILQ_INSERT_TAIL(&client6_iaidaddr.lease_list, sp, link); 282 /* for infinite lifetime don't do any timer */ 283 if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE || 284 sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) { 285 dprintf(LOG_INFO, "%s" "infinity address life time for %s", 286 FNAME, in6addr2str(&addr->addr, 0)); 287 return (0); 288 } 289 /* set up expired timer for lease*/ 290 if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) { 291 dprintf(LOG_ERR, "%s" "failed to add a timer for lease %s", 292 FNAME, in6addr2str(&addr->addr, 0)); 293 free(sp); 294 return (-1); 295 } 296 d = sp->lease_addr.preferlifetime; 297 timo.tv_sec = (long)d; 298 timo.tv_usec = 0; 299 dhcp6_set_timer(&timo, sp->timer); 300 return 0; 301} 302 303int 304dhcp6_remove_iaidaddr(struct dhcp6_iaidaddr *iaidaddr) 305{ 306 struct dhcp6_lease *lv, *lv_next; 307 for (lv = TAILQ_FIRST(&iaidaddr->lease_list); lv; lv = lv_next) { 308 lv_next = TAILQ_NEXT(lv, link); 309 (void)dhcp6_remove_lease(lv); 310 } 311 /* 312 if (iaidaddr->client6_info.serverid.duid_id != NULL) 313 duidfree(&iaidaddr->client6_info.serverid); 314 */ 315 if (iaidaddr->timer) 316 dhcp6_remove_timer(iaidaddr->timer); 317 TAILQ_INIT(&iaidaddr->lease_list); 318 return 0; 319} 320 321int 322dhcp6_remove_lease(struct dhcp6_lease *sp) 323{ 324 dprintf(LOG_DEBUG, "%s" "removing address %s", FNAME, 325 in6addr2str(&sp->lease_addr.addr, 0)); 326 sp->state = INVALID; 327 if (write_lease(sp, client6_lease_file) != 0) { 328 dprintf(LOG_INFO, "%s" 329 "failed to write removed lease address %s to lease file", 330 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 331 return (-1); 332 } 333 if (sp->lease_addr.type == IAPD) { 334 dprintf(LOG_INFO, "request prefix is %s/%d", 335 in6addr2str(&sp->lease_addr.addr, 0), sp->lease_addr.plen); 336 337 } else if (client6_ifaddrconf(IFADDRCONF_REMOVE, &sp->lease_addr) != 0) { 338 dprintf(LOG_INFO, "%s" "removing address %s failed", 339 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 340 } 341 /* remove expired timer for this lease. */ 342 if (sp->timer) 343 dhcp6_remove_timer(sp->timer); 344 TAILQ_REMOVE(&client6_iaidaddr.lease_list, sp, link); 345 free(sp); 346 /* can't remove expired iaidaddr even there is no lease in this iaidaddr 347 * since the rebind->solicit timer uses this iaidaddr 348 * if(TAILQ_EMPTY(&client6_iaidaddr.lease_list)) 349 * dhcp6_remove_iaidaddr(); 350 */ 351 352 /* WNR3500L TD192: 353 * Execute dhcp6c_down, so that LAN services and GUI 354 * are restarted correctly. 355 */ 356 char command[256]; 357 sprintf(command, "dhcp6c_down %s", get_dhcpc_dev_name()); 358 system(command); 359 360 return 0; 361} 362 363int 364dhcp6_update_iaidaddr(struct dhcp6_optinfo *optinfo, int flag) 365{ 366 struct dhcp6_listval *lv, *lv_next = NULL; 367 struct dhcp6_lease *cl, *cl_next; 368 struct timeval timo; 369 double d; 370 char command[256], command2[256]; 371 memset(command, 0, sizeof(command)); 372 373 if (client6_iaidaddr.client6_info.iaidinfo.renewtime > 374 client6_iaidaddr.client6_info.iaidinfo.rebindtime) { 375 dprintf(LOG_INFO, " T1 time is greater than T2 time"); 376 return (0); 377 } 378 if (flag == ADDR_REMOVE) { 379 for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) { 380 lv_next = TAILQ_NEXT(lv, link); 381 cl = dhcp6_find_lease(&client6_iaidaddr, &lv->val_dhcp6addr); 382 if (cl) { 383 /* remove leases */ 384 dhcp6_remove_lease(cl); 385 } 386 } 387 return 0; 388 } 389 /* flag == ADDR_UPDATE */ 390 for (lv = TAILQ_FIRST(&optinfo->addr_list); lv; lv = lv_next) { 391 lv_next = TAILQ_NEXT(lv, link); 392 if (lv->val_dhcp6addr.type != IAPD) { 393 lv->val_dhcp6addr.plen = 394 dhcp6_get_prefixlen(&lv->val_dhcp6addr.addr, dhcp6_if); 395 if (lv->val_dhcp6addr.plen == PREFIX_LEN_NOTINRA) { 396 dprintf(LOG_WARNING, "assigned address %s is not in any RAs" 397 " prefix length using 64 bit instead", 398 in6addr2str(&lv->val_dhcp6addr.addr, 0)); 399 sprintf(command, "dhcp6c_up %s %s %d ", 400 get_dhcpc_dev_name(), 401 in6addr2str(&lv->val_dhcp6addr.addr, 0), 402 lv->val_dhcp6addr.plen); 403 } 404 } 405 if ((cl = dhcp6_find_lease(&client6_iaidaddr, &lv->val_dhcp6addr)) != NULL) { 406 /* update leases */ 407 dhcp6_update_lease(&lv->val_dhcp6addr, cl); 408 continue; 409 } 410 /* need to add the new leases */ 411 if (dhcp6_add_lease(&lv->val_dhcp6addr)) { 412 dprintf(LOG_INFO, "%s" "failed to add a new addr lease %s", 413 FNAME, in6addr2str(&lv->val_dhcp6addr.addr, 0)); 414 continue; 415 } 416 continue; 417 } 418 /* remove leases that not on the updated list */ 419 for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl; cl = cl_next) { 420 cl_next = TAILQ_NEXT(cl, link); 421 lv = dhcp6_find_listval(&optinfo->addr_list, &cl->lease_addr, 422 DHCP6_LISTVAL_DHCP6ADDR); 423 /* remove leases that not on the updated list */ 424 if (lv == NULL) 425 dhcp6_remove_lease(cl); 426 } 427 /* update server id */ 428 if (client6_iaidaddr.state == REBIND) { 429 if (duidcpy(&client6_iaidaddr.client6_info.serverid, &optinfo->serverID)) { 430 dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); 431 return (-1); 432 } 433 } 434 if (TAILQ_EMPTY(&client6_iaidaddr.lease_list)) 435 return (0); 436 437 /* add new prefix (IAPD) */ 438 for (lv = TAILQ_FIRST(&optinfo->prefix_list); lv; lv = lv_next) { 439 lv_next = TAILQ_NEXT(lv, link); 440 if (lv->val_dhcp6addr.type == IAPD) { 441 sprintf(command2, " %s %d &", 442 in6addr2str(&lv->val_dhcp6addr.addr, 0), 443 lv->val_dhcp6addr.plen); 444 if (!strlen(command)) 445 sprintf(command, "dhcp6c_up %s ", get_dhcpc_dev_name()); 446 strcat(command, command2); 447 } 448 } 449 450 /* set up renew T1, rebind T2 timer renew/rebind based on iaid */ 451 /* Should we process IA_TA, IA_NA differently */ 452 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) { 453 u_int32_t min_plifetime; 454 min_plifetime = get_min_preferlifetime(&client6_iaidaddr); 455 if (min_plifetime == DHCP6_DURATITION_INFINITE) 456 client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime; 457 else 458 client6_iaidaddr.client6_info.iaidinfo.renewtime = min_plifetime / 2; 459 } 460 if (client6_iaidaddr.client6_info.iaidinfo.rebindtime == 0) { 461 client6_iaidaddr.client6_info.iaidinfo.rebindtime = 462 get_min_preferlifetime(&client6_iaidaddr) * 4 / 5; 463 } 464 dprintf(LOG_INFO, "renew time %d, rebind time %d", 465 client6_iaidaddr.client6_info.iaidinfo.renewtime, 466 client6_iaidaddr.client6_info.iaidinfo.rebindtime); 467 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == 0) 468 return (0); 469 if (client6_iaidaddr.client6_info.iaidinfo.renewtime == DHCP6_DURATITION_INFINITE) { 470 client6_iaidaddr.client6_info.iaidinfo.rebindtime = DHCP6_DURATITION_INFINITE; 471 if (client6_iaidaddr.timer) 472 dhcp6_remove_timer(client6_iaidaddr.timer); 473 return (0); 474 } 475 /* update the start date and timer */ 476 if (client6_iaidaddr.timer == NULL) { 477 if ((client6_iaidaddr.timer = 478 dhcp6_add_timer(dhcp6_iaidaddr_timo, &client6_iaidaddr)) == NULL) { 479 dprintf(LOG_ERR, "%s" "failed to add a timer for iaid %u", 480 FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid); 481 return (-1); 482 } 483 } 484 time(&client6_iaidaddr.start_date); 485 client6_iaidaddr.state = ACTIVE; 486 d = client6_iaidaddr.client6_info.iaidinfo.renewtime; 487 timo.tv_sec = (long)d; 488 timo.tv_usec = 0; 489 dhcp6_set_timer(&timo, client6_iaidaddr.timer); 490 491 /* Call our callback function to do something useful */ 492 if (strlen(command)) 493 system(command); 494 495 return 0; 496} 497 498static int 499dhcp6_update_lease(struct dhcp6_addr *addr, struct dhcp6_lease *sp) 500{ 501 struct timeval timo; 502 double d; 503 504 if (addr->status_code != DH6OPT_STCODE_SUCCESS && 505 addr->status_code != DH6OPT_STCODE_UNDEFINE) { 506 dprintf(LOG_ERR, "%s" "not successful status code for %s is %s", FNAME, 507 in6addr2str(&addr->addr, 0), dhcp6_stcodestr(addr->status_code)); 508 dhcp6_remove_lease(sp); 509 return (0); 510 } 511 /* remove leases with validlifetime == 0, and preferlifetime == 0 */ 512 if (addr->validlifetime == 0 || addr->preferlifetime == 0 || 513 addr->preferlifetime > addr->validlifetime) { 514 dprintf(LOG_ERR, "%s" "invalid address life time for %s", 515 FNAME, in6addr2str(&addr->addr, 0)); 516 dhcp6_remove_lease(sp); 517 return (0); 518 } 519 memcpy(&sp->lease_addr, addr, sizeof(sp->lease_addr)); 520 sp->state = ACTIVE; 521 time(&sp->start_date); 522 if (write_lease(sp, client6_lease_file) != 0) { 523 dprintf(LOG_ERR, "%s" 524 "failed to write an updated lease address %s to lease file", 525 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 526 return (-1); 527 } 528 if (sp->lease_addr.validlifetime == DHCP6_DURATITION_INFINITE || 529 sp->lease_addr.preferlifetime == DHCP6_DURATITION_INFINITE) { 530 dprintf(LOG_INFO, "%s" "infinity address life time for %s", 531 FNAME, in6addr2str(&addr->addr, 0)); 532 if (sp->timer) 533 dhcp6_remove_timer(sp->timer); 534 return (0); 535 } 536 if (sp->timer == NULL) { 537 if ((sp->timer = dhcp6_add_timer(dhcp6_lease_timo, sp)) == NULL) { 538 dprintf(LOG_ERR, "%s" "failed to add a timer for lease %s", 539 FNAME, in6addr2str(&addr->addr, 0)); 540 return (-1); 541 } 542 } 543 d = sp->lease_addr.preferlifetime; 544 timo.tv_sec = (long)d; 545 timo.tv_usec = 0; 546 dhcp6_set_timer(&timo, sp->timer); 547 return (0); 548} 549 550struct dhcp6_lease * 551dhcp6_find_lease(struct dhcp6_iaidaddr *iaidaddr, 552 struct dhcp6_addr *ifaddr) 553{ 554 struct dhcp6_lease *sp; 555 for (sp = TAILQ_FIRST(&iaidaddr->lease_list); sp; 556 sp = TAILQ_NEXT(sp, link)) { 557 /* sp->lease_addr.plen == ifaddr->plen */ 558 dprintf(LOG_DEBUG, "%s" "get address is %s/%d ", FNAME, 559 in6addr2str(&ifaddr->addr, 0), ifaddr->plen); 560 dprintf(LOG_DEBUG, "%s" "lease address is %s/%d ", FNAME, 561 in6addr2str(&sp->lease_addr.addr, 0), ifaddr->plen); 562 if (IN6_ARE_ADDR_EQUAL(&sp->lease_addr.addr, &ifaddr->addr)) { 563 if (sp->lease_addr.type == IAPD) { 564 if (sp->lease_addr.plen == ifaddr->plen) 565 return (sp); 566 } else if (sp->lease_addr.type == IANA || 567 sp->lease_addr.type == IATA) 568 return (sp); 569 } 570 } 571 return (NULL); 572} 573 574struct dhcp6_timer * 575dhcp6_iaidaddr_timo(void *arg) 576{ 577 struct dhcp6_iaidaddr *sp = (struct dhcp6_iaidaddr *)arg; 578 struct dhcp6_event *ev; 579 struct timeval timeo; 580 int dhcpstate; 581 double d = 0; 582 583 dprintf(LOG_DEBUG, "client6_iaidaddr timeout for %d, state=%d", 584 client6_iaidaddr.client6_info.iaidinfo.iaid, sp->state); 585 586 dhcp6_clear_list(&request_list); 587 TAILQ_INIT(&request_list); 588 /* ToDo: what kind of opiton Request value, client would like to pass? */ 589 switch(sp->state) { 590 case ACTIVE: 591 sp->state = RENEW; 592 dhcpstate = DHCP6S_RENEW; 593 d = sp->client6_info.iaidinfo.rebindtime - sp->client6_info.iaidinfo.renewtime; 594 timeo.tv_sec = (long)d; 595 timeo.tv_usec = 0; 596 break; 597 case RENEW: 598 sp->state = REBIND; 599 dhcpstate = DHCP6S_REBIND; 600 d = get_max_validlifetime(&client6_iaidaddr) - 601 sp->client6_info.iaidinfo.rebindtime; 602 timeo.tv_sec = (long)d; 603 timeo.tv_usec = 0; 604 if (sp->client6_info.serverid.duid_id != NULL) 605 duidfree(&sp->client6_info.serverid); 606 break; 607 case REBIND: 608 dprintf(LOG_INFO, "%s" "failed to rebind a client6_iaidaddr %d" 609 " go to solicit and request new ipv6 addresses", 610 FNAME, client6_iaidaddr.client6_info.iaidinfo.iaid); 611 sp->state = INVALID; 612 dhcpstate = DHCP6S_SOLICIT; 613 free_servers(sp->ifp); 614 break; 615 default: 616 return (NULL); 617 } 618 if ((ev = dhcp6_create_event(sp->ifp, dhcpstate)) == NULL) { 619 dprintf(LOG_ERR, "%s" "failed to create a new event", 620 FNAME); 621 return (NULL); 622 } 623 switch(sp->state) { 624 case RENEW: 625 if (duidcpy(&ev->serverid, &sp->client6_info.serverid)) { 626 dprintf(LOG_ERR, "%s" "failed to copy server ID", FNAME); 627 free(ev); 628 return (NULL); 629 } 630 case REBIND: 631 /* BUG: d not set! */ 632 ev->max_retrans_dur = d; 633 break; 634 default: 635 break; 636 } 637 if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) { 638 dprintf(LOG_ERR, "%s" "failed to create a new event timer", FNAME); 639 if (sp->state == RENEW) 640 duidfree(&ev->serverid); 641 free(ev); 642 return (NULL); 643 } 644 TAILQ_INSERT_TAIL(&sp->ifp->event_list, ev, link); 645 if (sp->state != INVALID) { 646 struct dhcp6_lease *cl; 647 /* create an address list for renew and rebind */ 648 for (cl = TAILQ_FIRST(&client6_iaidaddr.lease_list); cl; 649 cl = TAILQ_NEXT(cl, link)) { 650 struct dhcp6_listval *lv; 651 /* IA_NA address */ 652 if ((lv = malloc(sizeof(*lv))) == NULL) { 653 dprintf(LOG_ERR, "%s" 654 "failed to allocate memory for an ipv6 addr", FNAME); 655 if (sp->state == RENEW) 656 duidfree(&ev->serverid); 657 free(ev->timer); 658 free(ev); 659 return (NULL); 660 } 661 memcpy(&lv->val_dhcp6addr, &cl->lease_addr, 662 sizeof(lv->val_dhcp6addr)); 663 lv->val_dhcp6addr.status_code = DH6OPT_STCODE_UNDEFINE; 664 TAILQ_INSERT_TAIL(&request_list, lv, link); 665 } 666 dhcp6_set_timer(&timeo, sp->timer); 667 } else { 668 dhcp6_remove_iaidaddr(&client6_iaidaddr); 669 /* remove event data for that event */ 670 sp->timer = NULL; 671 } 672 ev->timeouts = 0; 673 dhcp6_set_timeoparam(ev); 674 dhcp6_reset_timer(ev); 675 client6_send(ev); 676 return (sp->timer); 677} 678 679 680struct dhcp6_timer * 681dhcp6_lease_timo(void *arg) 682{ 683 struct dhcp6_lease *sp = (struct dhcp6_lease *)arg; 684 struct timeval timeo; 685 double d; 686 687 dprintf(LOG_DEBUG, "%s" "lease timeout for %s, state=%d", FNAME, 688 in6addr2str(&sp->lease_addr.addr, 0), sp->state); 689 /* cancel the current event for this lease */ 690 if (sp->state == INVALID) { 691 dprintf(LOG_INFO, "%s" "failed to remove an addr %s", 692 FNAME, in6addr2str(&sp->lease_addr.addr, 0)); 693 dhcp6_remove_lease(sp); 694 return (NULL); 695 } 696 switch(sp->state) { 697 case ACTIVE: 698 sp->state = EXPIRED; 699 d = sp->lease_addr.validlifetime - sp->lease_addr.preferlifetime; 700 timeo.tv_sec = (long)d; 701 timeo.tv_usec = 0; 702 dhcp6_set_timer(&timeo, sp->timer); 703 break; 704 case EXPIRED: 705 sp->state = INVALID; 706 dhcp6_remove_lease(sp); 707 default: 708 return (NULL); 709 } 710 return (sp->timer); 711} 712 713int 714client6_ifaddrconf(ifaddrconf_cmd_t cmd, struct dhcp6_addr *ifaddr) 715{ 716 struct in6_ifreq req; 717 struct dhcp6_if *ifp = client6_iaidaddr.ifp; 718 unsigned long ioctl_cmd; 719 char *cmdstr; 720 int s, errno; 721 722 switch(cmd) { 723 case IFADDRCONF_ADD: 724 cmdstr = "add"; 725 ioctl_cmd = SIOCSIFADDR; 726 break; 727 case IFADDRCONF_REMOVE: 728 cmdstr = "remove"; 729 ioctl_cmd = SIOCDIFADDR; 730 break; 731 default: 732 return (-1); 733 } 734 735 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 736 dprintf(LOG_ERR, "%s" "can't open a temporary socket: %s", 737 FNAME, strerror(errno)); 738 return (-1); 739 } 740 memset(&req, 0, sizeof(req)); 741 req.ifr6_ifindex = if_nametoindex(ifp->ifname); 742 memcpy(&req.ifr6_addr, &ifaddr->addr, sizeof(req.ifr6_addr)); 743 744 req.ifr6_prefixlen = ifaddr->plen; 745 746 if (ioctl(s, ioctl_cmd, &req) && errno != EEXIST) { 747 dprintf(LOG_NOTICE, "%s" "failed to %s an address on %s: %s", 748 FNAME, cmdstr, ifp->ifname, strerror(errno)); 749 close(s); 750 return (-1); 751 } 752 753 dprintf(LOG_DEBUG, "%s" "%s an address %s on %s", FNAME, cmdstr, 754 in6addr2str(&ifaddr->addr, 0), ifp->ifname); 755 close(s); 756 return (0); 757} 758 759 760int 761get_iaid(const char *ifname, const struct iaid_table *iaidtab, int num_device) 762{ 763 struct hardware hdaddr; 764 struct iaid_table *temp = (struct iaid_table *)iaidtab; 765 int i; 766 hdaddr.len = gethwid(hdaddr.data, 6, ifname, &hdaddr.type); 767 for (i = 0; i < num_device; i++, temp++) { 768 if (!memcmp(temp->hwaddr.data, hdaddr.data, temp->hwaddr.len) 769 && hdaddr.len == temp->hwaddr.len && hdaddr.type == temp->hwaddr.type) { 770 dprintf(LOG_DEBUG, "%s"" found interface %s iaid %u", 771 FNAME, ifname, temp->iaid); 772 return temp->iaid; 773 } else 774 continue; 775 } 776 return 0; 777} 778 779int 780create_iaid(struct iaid_table *iaidtab, int num_device) 781{ 782 struct iaid_table *temp = iaidtab; 783 char buff[1024]; 784 struct ifconf ifc; 785 struct ifreq *ifr; 786 int i; 787 788 ifc.ifc_len = sizeof(buff); 789 ifc.ifc_buf = buff; 790 if (ioctl(nlsock, SIOCGIFCONF, &ifc) < 0) { 791 dprintf(LOG_ERR, "%s" "ioctl SIOCGIFCONF", FNAME); 792 return -1; 793 } 794 795 ifr = ifc.ifc_req; 796 for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0 && num_device < MAX_DEVICE; 797 ifr++) { 798 if (!strcmp(ifr->ifr_name, "lo")) continue; 799 temp->hwaddr.len = gethwid(temp->hwaddr.data, sizeof(temp->hwaddr.data), ifr->ifr_name, &temp->hwaddr.type); 800 switch (temp->hwaddr.type) { 801 case ARPHRD_ETHER: 802 case ARPHRD_IEEE802: 803 memcpy(&temp->iaid, temp->hwaddr.data, sizeof(temp->iaid)); 804 break; 805 case ARPHRD_PPP: 806 temp->iaid = do_hash(ifr->ifr_name,sizeof(ifr->ifr_name)) 807 + if_nametoindex(ifr->ifr_name); 808 break; 809 default: 810 dprintf(LOG_INFO, "doesn't support %s address family %d", 811 ifr->ifr_name, temp->hwaddr.type); 812 continue; 813 } 814 dprintf(LOG_DEBUG, "%s"" create iaid %u for interface %s", 815 FNAME, temp->iaid, ifr->ifr_name); 816 num_device++; 817 temp++; 818 } 819 return num_device; 820} 821