1/* 2 * Copyright (c) 2009-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * DHCPv6Client.c 26 * - API's to instantiate and interact with DHCPv6Client 27 */ 28 29/* 30 * Modification History 31 * 32 * September 22, 2009 Dieter Siegmund (dieter@apple.com) 33 * - created 34 * 35 * May 14, 2010 Dieter Siegmund (dieter@apple.com) 36 * - implemented stateful support 37 */ 38 39#include <stdio.h> 40#include <stdlib.h> 41#include <unistd.h> 42#include <stddef.h> 43#include <sys/types.h> 44#include <sys/socket.h> 45#include <net/if.h> 46#include <mach/boolean.h> 47#include <string.h> 48#include <errno.h> 49#include <arpa/inet.h> 50#include "DHCPv6.h" 51#include "DHCPv6Client.h" 52#include "DHCPv6Options.h" 53#include "DHCPv6Socket.h" 54#include "DHCPDUIDIAID.h" 55#include "timer.h" 56#include "ipconfigd_threads.h" 57#include "interfaces.h" 58#include "cfutil.h" 59#include "DNSNameList.h" 60#include "symbol_scope.h" 61#include "ipconfigd_threads.h" 62 63typedef void 64(DHCPv6ClientEventFunc)(DHCPv6ClientRef client, IFEventID_t event_id, 65 void * event_data); 66typedef DHCPv6ClientEventFunc * DHCPv6ClientEventFuncRef; 67 68typedef enum { 69 kDHCPv6ClientStateNone = 0, 70 kDHCPv6ClientStateSolicit, 71 kDHCPv6ClientStateRequest, 72 kDHCPv6ClientStateBound, 73 kDHCPv6ClientStateRenew, 74 kDHCPv6ClientStateRebind, 75 kDHCPv6ClientStateConfirm, 76 kDHCPv6ClientStateRelease, 77 kDHCPv6ClientStateUnbound, 78 kDHCPv6ClientStateDecline, 79 kDHCPv6ClientStateInform, 80 kDHCPv6ClientStateLast 81} DHCPv6ClientState; 82 83STATIC const char * 84DHCPv6ClientStateGetName(DHCPv6ClientState cstate) 85{ 86 STATIC const char * names[] = { 87 "None", 88 "Solicit", 89 "Request", 90 "Bound", 91 "Renew", 92 "Rebind", 93 "Confirm", 94 "Release", 95 "Unbound", 96 "Decline", 97 "Inform", 98 }; 99 100 if (cstate >= kDHCPv6ClientStateNone 101 && cstate < kDHCPv6ClientStateLast) { 102 return (names[cstate]); 103 } 104 return ("Unknown"); 105} 106 107typedef struct { 108 CFAbsoluteTime start; 109 110 /* these times are all relative to start */ 111 uint32_t t1; 112 uint32_t t2; 113 uint32_t valid_lifetime; 114 uint32_t preferred_lifetime; 115 bool valid; 116} lease_info_t; 117 118struct DHCPv6Client { 119 CFRunLoopSourceRef callback_rls; 120 DHCPv6ClientNotificationCallBack callback; 121 void * callback_arg; 122 struct in6_addr our_ip; 123 int our_prefix_length; 124 DHCPv6ClientState cstate; 125 DHCPv6SocketRef sock; 126 timer_callout_t * timer; 127 DHCPv6ClientEventFuncRef address_changed_func; 128 uint32_t transaction_id; 129 int try; 130 CFAbsoluteTime start_time; 131 CFTimeInterval retransmit_time; 132 dhcpv6_info_t saved; 133 bool saved_verified; 134 DHCPDUIDRef server_id; /* points to saved */ 135 DHCPv6OptionIA_NARef ia_na; /* points to saved */ 136 DHCPv6OptionIAADDRRef ia_addr; /* points to saved */ 137 lease_info_t lease; 138}; 139 140STATIC DHCPv6ClientEventFunc DHCPv6Client_Bound; 141STATIC DHCPv6ClientEventFunc DHCPv6Client_Unbound; 142STATIC DHCPv6ClientEventFunc DHCPv6Client_Solicit; 143STATIC DHCPv6ClientEventFunc DHCPv6Client_Request; 144STATIC DHCPv6ClientEventFunc DHCPv6Client_RenewRebind; 145 146INLINE void 147DHCPv6ClientSetAddressChangedFunc(DHCPv6ClientRef client, 148 DHCPv6ClientEventFuncRef func) 149{ 150 client->address_changed_func = func; 151 return; 152} 153 154INLINE interface_t * 155DHCPv6ClientGetInterface(DHCPv6ClientRef client) 156{ 157 return (DHCPv6SocketGetInterface(client->sock)); 158} 159 160STATIC const struct sockaddr_in6 dhcpv6_all_servers_and_relay_agents = { 161 sizeof(dhcpv6_all_servers_and_relay_agents), 162 AF_INET6, 0, 0, 163 All_DHCP_Relay_Agents_and_Servers_INIT, 0 164}; 165 166STATIC const uint16_t DHCPv6RequestedOptionsStatic[] = { 167 kDHCPv6OPTION_DNS_SERVERS, 168 kDHCPv6OPTION_DOMAIN_LIST 169}; 170#define kDHCPv6RequestedOptionsStaticCount (sizeof(DHCPv6RequestedOptionsStatic) / sizeof(DHCPv6RequestedOptionsStatic[0])) 171 172STATIC uint16_t * DHCPv6RequestedOptionsDefault = (uint16_t *)DHCPv6RequestedOptionsStatic; 173STATIC int DHCPv6RequestedOptionsDefaultCount = kDHCPv6RequestedOptionsStaticCount; 174 175STATIC uint16_t * DHCPv6RequestedOptions = (uint16_t *)DHCPv6RequestedOptionsStatic; 176STATIC int DHCPv6RequestedOptionsCount = kDHCPv6RequestedOptionsStaticCount; 177 178STATIC int 179S_get_prefix_length(const struct in6_addr * addr, int if_index) 180{ 181 int prefix_length; 182 183 prefix_length = inet6_get_prefix_length(addr, if_index); 184 if (prefix_length == 0) { 185#define DHCPV6_PREFIX_LENGTH 128 186 prefix_length = DHCPV6_PREFIX_LENGTH; 187 } 188 return (prefix_length); 189} 190 191PRIVATE_EXTERN void 192DHCPv6ClientSetRequestedOptions(uint16_t * requested_options, 193 int requested_options_count) 194{ 195 if (requested_options != NULL && requested_options_count != 0) { 196 DHCPv6RequestedOptionsDefault = requested_options; 197 DHCPv6RequestedOptionsDefaultCount = requested_options_count; 198 } 199 else { 200 DHCPv6RequestedOptionsDefault 201 = (uint16_t *)DHCPv6RequestedOptionsStatic; 202 DHCPv6RequestedOptionsDefaultCount 203 = kDHCPv6RequestedOptionsStaticCount; 204 } 205 DHCPv6RequestedOptions = DHCPv6RequestedOptionsDefault; 206 DHCPv6RequestedOptionsCount = DHCPv6RequestedOptionsDefaultCount; 207 return; 208} 209 210bool 211DHCPv6ClientOptionIsOK(int option) 212{ 213 int i; 214 215 switch (option) { 216 case kDHCPv6OPTION_CLIENTID: 217 case kDHCPv6OPTION_SERVERID: 218 case kDHCPv6OPTION_ORO: 219 case kDHCPv6OPTION_ELAPSED_TIME: 220 case kDHCPv6OPTION_UNICAST: 221 case kDHCPv6OPTION_RAPID_COMMIT: 222 case kDHCPv6OPTION_IA_NA: 223 case kDHCPv6OPTION_IAADDR: 224 case kDHCPv6OPTION_STATUS_CODE: 225 case kDHCPv6OPTION_IA_TA: 226 case kDHCPv6OPTION_PREFERENCE: 227 case kDHCPv6OPTION_RELAY_MSG: 228 case kDHCPv6OPTION_AUTH: 229 case kDHCPv6OPTION_USER_CLASS: 230 case kDHCPv6OPTION_VENDOR_CLASS: 231 case kDHCPv6OPTION_VENDOR_OPTS: 232 case kDHCPv6OPTION_INTERFACE_ID: 233 case kDHCPv6OPTION_RECONF_MSG: 234 case kDHCPv6OPTION_RECONF_ACCEPT: 235 return (TRUE); 236 default: 237 break; 238 } 239 240 for (i = 0; i < DHCPv6RequestedOptionsCount; i++) { 241 if (DHCPv6RequestedOptions[i] == option) { 242 return (TRUE); 243 } 244 } 245 return (FALSE); 246} 247 248STATIC double 249random_double_in_range(double bottom, double top) 250{ 251 double r = (double)arc4random() / (double)UINT32_MAX; 252 253 return (bottom + (top - bottom) * r); 254} 255 256STATIC uint32_t 257get_new_transaction_id(void) 258{ 259 uint32_t r = arc4random(); 260 261#define LOWER_24_BITS ((uint32_t)0x00ffffff) 262 /* return the lower 24 bits */ 263 return (r & LOWER_24_BITS); 264} 265 266STATIC bool 267S_insert_duid(DHCPv6OptionAreaRef oa_p) 268{ 269 CFDataRef data; 270 DHCPv6OptionErrorString err; 271 272 data = DHCPDUIDGet(get_interface_list()); 273 if (data == NULL) { 274 return (FALSE); 275 } 276 if (DHCPv6OptionAreaAddOption(oa_p, kDHCPv6OPTION_CLIENTID, 277 CFDataGetLength(data), CFDataGetBytePtr(data), 278 &err) == FALSE) { 279 my_log(LOG_NOTICE, "DHCPv6Client: failed to add CLIENTID, %s", 280 err.str); 281 return (FALSE); 282 } 283 return (TRUE); 284} 285 286STATIC bool 287S_duid_matches(DHCPv6OptionListRef options) 288{ 289 CFDataRef data; 290 DHCPDUIDRef duid; 291 int option_len; 292 293 data = DHCPDUIDGet(get_interface_list()); 294 duid = (DHCPDUIDRef) 295 DHCPv6OptionListGetOptionDataAndLength(options, 296 kDHCPv6OPTION_CLIENTID, 297 &option_len, NULL); 298 if (duid == NULL 299 || CFDataGetLength(data) != option_len 300 || bcmp(duid, CFDataGetBytePtr(data), option_len) != 0) { 301 return (FALSE); 302 } 303 return (TRUE); 304} 305 306STATIC DHCPv6OptionIA_NARef 307get_ia_na_addr(DHCPv6ClientRef client, int msg_type, 308 DHCPv6OptionListRef options, 309 DHCPv6OptionIAADDRRef * ret_ia_addr) 310{ 311 DHCPv6OptionErrorString err; 312 DHCPv6OptionIA_NARef ia_na; 313 DHCPv6OptionListRef ia_na_options; 314 interface_t * if_p = DHCPv6ClientGetInterface(client); 315 int option_len; 316 uint32_t preferred_lifetime; 317 int start_index; 318 uint32_t t1; 319 uint32_t t2; 320 uint32_t valid_lifetime; 321 322 *ret_ia_addr = NULL; 323 ia_na = (DHCPv6OptionIA_NARef) 324 DHCPv6OptionListGetOptionDataAndLength(options, 325 kDHCPv6OPTION_IA_NA, 326 &option_len, NULL); 327 if (ia_na == NULL 328 || option_len <= DHCPv6OptionIA_NA_MIN_LENGTH) { 329 /* no IA_NA option */ 330 return (NULL); 331 } 332 t1 = DHCPv6OptionIA_NAGetT1(ia_na); 333 t2 = DHCPv6OptionIA_NAGetT2(ia_na); 334 if (t1 != 0 && t2 != 0) { 335 if (t1 > t2) { 336 /* server is confused */ 337 return (NULL); 338 } 339 } 340 option_len -= DHCPv6OptionIA_NA_MIN_LENGTH; 341 ia_na_options = DHCPv6OptionListCreate(ia_na->options, option_len, &err); 342 if (ia_na_options == NULL) { 343 my_log(LOG_DEBUG, 344 "DHCPv6 %s: %s IA_NA contains no options", 345 if_name(if_p), DHCPv6MessageName(msg_type)); 346 /* no options */ 347 return (NULL); 348 } 349 /* find the first ia_addr with non-zero lifetime */ 350 start_index = 0; 351 for (start_index = 0; TRUE; start_index++) { 352 DHCPv6OptionIAADDRRef ia_addr; 353 354 ia_addr = (DHCPv6OptionIAADDRRef) 355 DHCPv6OptionListGetOptionDataAndLength(ia_na_options, 356 kDHCPv6OPTION_IAADDR, 357 &option_len, &start_index); 358 if (ia_addr == NULL 359 || option_len < DHCPv6OptionIAADDR_MIN_LENGTH) { 360 my_log(LOG_DEBUG, 361 "DHCPv6 %s: %s IA_NA contains no valid IAADDR option", 362 if_name(if_p), DHCPv6MessageName(msg_type)); 363 /* missing/invalid IAADDR option */ 364 break; 365 } 366 valid_lifetime = DHCPv6OptionIAADDRGetValidLifetime(ia_addr); 367 preferred_lifetime 368 = DHCPv6OptionIAADDRGetPreferredLifetime(ia_addr); 369 if (valid_lifetime == 0) { 370 my_log(LOG_DEBUG, 371 "DHCP %s: %s IA_ADDR has valid/preferred lifetime is 0," 372 " skipping", 373 if_name(if_p), DHCPv6MessageName(msg_type)); 374 } 375 else if (preferred_lifetime > valid_lifetime) { 376 /* server is confused */ 377 my_log(LOG_DEBUG, 378 "DHCP %s: %s IA_ADDR preferred %d > valid lifetime", 379 if_name(if_p), DHCPv6MessageName(msg_type), 380 preferred_lifetime, valid_lifetime); 381 break; 382 } 383 else { 384 *ret_ia_addr = ia_addr; 385 break; 386 } 387 } 388 389 DHCPv6OptionListRelease(&ia_na_options); 390 391 /* if we didn't find a suitable IAADDR, then ignore the IA_NA */ 392 if (*ret_ia_addr == NULL) { 393 ia_na = NULL; 394 } 395 return (ia_na); 396} 397 398STATIC uint8_t 399get_preference_value_from_options(DHCPv6OptionListRef options) 400{ 401 int option_len; 402 DHCPv6OptionPREFERENCERef pref; 403 uint8_t value = kDHCPv6OptionPREFERENCEMinValue; 404 405 pref = (DHCPv6OptionPREFERENCERef) 406 DHCPv6OptionListGetOptionDataAndLength(options, 407 kDHCPv6OPTION_PREFERENCE, 408 &option_len, NULL); 409 if (pref != NULL 410 && option_len >= DHCPv6OptionPREFERENCE_MIN_LENGTH) { 411 value = pref->value; 412 } 413 return (value); 414} 415 416#define OUR_IA_NA_SIZE (DHCPv6OptionIA_NA_MIN_LENGTH + DHCPV6_OPTION_HEADER_SIZE + DHCPv6OptionIAADDR_MIN_LENGTH) 417 418STATIC bool 419add_ia_na_option(DHCPv6ClientRef client, DHCPv6OptionAreaRef oa_p, 420 DHCPv6OptionErrorStringRef err_p) 421{ 422 char buf[OUR_IA_NA_SIZE]; 423 DHCPv6OptionIA_NARef ia_na_p; 424 DHCPv6OptionRef option; 425 DHCPv6OptionIAADDRRef ia_addr_p; 426 interface_t * if_p = DHCPv6ClientGetInterface(client); 427 428 ia_na_p = (DHCPv6OptionIA_NARef)buf; 429 DHCPv6OptionIA_NASetIAID(ia_na_p, DHCPIAIDGet(if_name(if_p))); 430 DHCPv6OptionIA_NASetT1(ia_na_p, 0); 431 DHCPv6OptionIA_NASetT2(ia_na_p, 0); 432 option = (DHCPv6OptionRef)(buf + DHCPv6OptionIA_NA_MIN_LENGTH); 433 DHCPv6OptionSetCode(option, kDHCPv6OPTION_IAADDR); 434 DHCPv6OptionSetLength(option, DHCPv6OptionIAADDR_MIN_LENGTH); 435 ia_addr_p = (DHCPv6OptionIAADDRRef) 436 (((char *)option) + DHCPV6_OPTION_HEADER_SIZE); 437 DHCPv6OptionIAADDRSetAddress(ia_addr_p, 438 DHCPv6OptionIAADDRGetAddress(client->ia_addr)); 439 DHCPv6OptionIAADDRSetPreferredLifetime(ia_addr_p, 0); 440 DHCPv6OptionIAADDRSetValidLifetime(ia_addr_p, 0); 441 return (DHCPv6OptionAreaAddOption(oa_p, kDHCPv6OPTION_IA_NA, 442 OUR_IA_NA_SIZE, ia_na_p, err_p)); 443} 444 445 446/* 447 * Function: option_data_get_length 448 * Purpose: 449 * Given a pointer to the option data, return its length, which is stored 450 * in the previous 2 bytes. 451 */ 452STATIC int 453option_data_get_length(const void * option_data) 454{ 455 const uint16_t * len_p; 456 457 len_p = (const uint16_t *)(option_data - sizeof(uint16_t)); 458 return (ntohs(*len_p)); 459} 460 461STATIC CFTimeInterval 462DHCPv6_RAND(void) 463{ 464 return (random_double_in_range(-0.1, 0.1)); 465} 466 467STATIC CFTimeInterval 468DHCPv6SubsequentTimeout(CFTimeInterval RTprev, CFTimeInterval MRT) 469{ 470 CFTimeInterval RT; 471 472 RT = 2 * RTprev + DHCPv6_RAND() * RTprev; 473 if (MRT != 0 && RT > MRT) { 474 RT = MRT + DHCPv6_RAND() * MRT; 475 } 476 return (RT); 477} 478 479STATIC CFTimeInterval 480DHCPv6InitialTimeout(CFTimeInterval IRT) 481{ 482 return (IRT + DHCPv6_RAND() * IRT); 483} 484 485STATIC uint16_t 486get_elapsed_time(DHCPv6ClientRef client) 487{ 488 uint16_t elapsed_time; 489 490 if (client->try == 1) { 491 elapsed_time = 0; 492 } 493 else { 494 uint32_t elapsed; 495 496 /* elapsed time is in 1/100ths of a second */ 497 elapsed = (timer_get_current_time() - client->start_time) * 100; 498#define MAX_ELAPSED 0xffff 499 if (elapsed > MAX_ELAPSED) { 500 elapsed_time = MAX_ELAPSED; 501 } 502 else { 503 elapsed_time = htons(elapsed); 504 } 505 } 506 return (elapsed_time); 507} 508 509/** 510 ** DHCPv6Client routines 511 **/ 512STATIC void 513DHCPv6ClientSetState(DHCPv6ClientRef client, DHCPv6ClientState cstate) 514{ 515 interface_t * if_p = DHCPv6ClientGetInterface(client); 516 517 client->cstate = cstate; 518 my_log(LOG_DEBUG, "DHCPv6 %s: %s", if_name(if_p), 519 DHCPv6ClientStateGetName(cstate)); 520} 521 522STATIC void 523DHCPv6ClientRemoveAddress(DHCPv6ClientRef client, const char * label) 524{ 525 interface_t * if_p = DHCPv6ClientGetInterface(client); 526 char ntopbuf[INET6_ADDRSTRLEN]; 527 int s; 528 529 if (IN6_IS_ADDR_UNSPECIFIED(&client->our_ip)) { 530 return; 531 } 532 if (G_IPConfiguration_verbose) { 533 my_log(LOG_DEBUG, "DHCPv6 %s: %s: removing %s", 534 if_name(if_p), label, 535 inet_ntop(AF_INET6, &client->our_ip, 536 ntopbuf, sizeof(ntopbuf))); 537 } 538 s = inet6_dgram_socket(); 539 if (s < 0) { 540 my_log(LOG_ERR, 541 "DHCPv6ClientRemoveAddress(%s):socket() failed, %s (%d)", 542 if_name(if_p), strerror(errno), errno); 543 } 544 else { 545 if (inet6_difaddr(s, if_name(if_p), &client->our_ip) < 0) { 546 my_log(LOG_DEBUG, 547 "DHCPv6ClientRemoveAddress(%s): remove %s failed, %s (%d)", 548 if_name(if_p), 549 inet_ntop(AF_INET6, &client->our_ip, 550 ntopbuf, sizeof(ntopbuf)), 551 strerror(errno), errno); 552 } 553 close(s); 554 } 555 bzero(&client->our_ip, sizeof(client->our_ip)); 556 client->our_prefix_length = 0; 557 return; 558} 559 560STATIC void 561DHCPv6ClientClearRetransmit(DHCPv6ClientRef client) 562{ 563 client->try = 0; 564 return; 565} 566 567STATIC CFTimeInterval 568DHCPv6ClientNextRetransmit(DHCPv6ClientRef client, 569 CFTimeInterval IRT, CFTimeInterval MRT) 570{ 571 client->try++; 572 if (client->try == 1) { 573 client->retransmit_time = DHCPv6InitialTimeout(IRT); 574 } 575 else { 576 client->retransmit_time 577 = DHCPv6SubsequentTimeout(client->retransmit_time, MRT); 578 } 579 return (client->retransmit_time); 580} 581 582STATIC void 583DHCPv6ClientPostNotification(DHCPv6ClientRef client) 584{ 585 if (client->callback_rls != NULL) { 586 CFRunLoopSourceSignal(client->callback_rls); 587 } 588 return; 589} 590 591STATIC void 592DHCPv6ClientCancelPendingEvents(DHCPv6ClientRef client) 593{ 594 DHCPv6ClientSetAddressChangedFunc(client, NULL); 595 DHCPv6SocketDisableReceive(client->sock); 596 timer_cancel(client->timer); 597 return; 598} 599 600STATIC void 601DHCPv6ClientClearPacket(DHCPv6ClientRef client) 602{ 603 if (client->saved.pkt_len == 0) { 604 return; 605 } 606 bzero(&client->lease, sizeof(client->lease)); 607 client->saved.pkt_len = 0; 608 if (client->saved.pkt != NULL) { 609 free(client->saved.pkt); 610 client->saved.pkt = NULL; 611 } 612 DHCPv6OptionListRelease(&client->saved.options); 613 client->server_id = NULL; 614 client->ia_na = NULL; 615 client->ia_addr = NULL; 616 client->saved_verified = FALSE; 617 return; 618} 619 620STATIC void 621DHCPv6ClientInactive(DHCPv6ClientRef client) 622{ 623 DHCPv6ClientCancelPendingEvents(client); 624 DHCPv6ClientClearPacket(client); 625 DHCPv6ClientRemoveAddress(client, "Inactive"); 626 DHCPv6ClientPostNotification(client); 627 return; 628} 629 630STATIC bool 631DHCPv6ClientLeaseStillValid(DHCPv6ClientRef client) 632{ 633 CFAbsoluteTime current_time; 634 lease_info_t * lease_p = &client->lease; 635 636 if (lease_p->valid == FALSE) { 637 goto done; 638 } 639 if (lease_p->valid_lifetime == DHCP_INFINITE_LEASE) { 640 goto done; 641 } 642 current_time = timer_get_current_time(); 643 if (current_time < lease_p->start) { 644 /* time went backwards */ 645 DHCPv6ClientClearPacket(client); 646 lease_p->valid = FALSE; 647 goto done; 648 } 649 if ((current_time - lease_p->start) >= lease_p->valid_lifetime) { 650 /* expired */ 651 DHCPv6ClientClearPacket(client); 652 lease_p->valid = FALSE; 653 } 654 655 done: 656 return (lease_p->valid); 657} 658 659STATIC void 660DHCPv6ClientSavePacket(DHCPv6ClientRef client, DHCPv6SocketReceiveDataRef data) 661{ 662 CFAbsoluteTime current_time = timer_get_current_time(); 663 DHCPv6OptionErrorString err; 664 lease_info_t * lease_p = &client->lease; 665 int option_len; 666 uint32_t preferred_lifetime; 667 uint32_t t1; 668 uint32_t t2; 669 uint32_t valid_lifetime; 670 671 DHCPv6ClientClearPacket(client); 672 client->saved.pkt_len = data->pkt_len; 673 client->saved.pkt = malloc(client->saved.pkt_len); 674 bcopy(data->pkt, client->saved.pkt, client->saved.pkt_len); 675 client->saved.options 676 = DHCPv6OptionListCreateWithPacket(client->saved.pkt, 677 client->saved.pkt_len, &err); 678 client->server_id = (DHCPDUIDRef) 679 DHCPv6OptionListGetOptionDataAndLength(client->saved.options, 680 kDHCPv6OPTION_SERVERID, 681 &option_len, NULL); 682 client->ia_na = get_ia_na_addr(client, client->saved.pkt->msg_type, 683 client->saved.options, &client->ia_addr); 684 if (client->ia_na != NULL) { 685 t1 = DHCPv6OptionIA_NAGetT1(client->ia_na); 686 t2 = DHCPv6OptionIA_NAGetT2(client->ia_na); 687 valid_lifetime = DHCPv6OptionIAADDRGetValidLifetime(client->ia_addr); 688 preferred_lifetime 689 = DHCPv6OptionIAADDRGetPreferredLifetime(client->ia_addr); 690 if (preferred_lifetime == 0) { 691 preferred_lifetime = valid_lifetime; 692 } 693 if (t1 == 0 || t2 == 0) { 694 if (preferred_lifetime == DHCP_INFINITE_LEASE) { 695 t1 = t2 = 0; 696 } 697 else { 698 t1 = preferred_lifetime * 0.5; 699 t2 = preferred_lifetime * 0.8; 700 } 701 } 702 else if (t1 == DHCP_INFINITE_LEASE || t2 == DHCP_INFINITE_LEASE) { 703 t1 = t2 = 0; 704 preferred_lifetime = DHCP_INFINITE_LEASE; 705 valid_lifetime = DHCP_INFINITE_LEASE; 706 } 707 lease_p->start = current_time; 708 if (valid_lifetime == DHCP_INFINITE_LEASE) { 709 lease_p->t1 = lease_p->t2 = 0; 710 preferred_lifetime = DHCP_INFINITE_LEASE; 711 } 712 else { 713 lease_p->t1 = t1; 714 lease_p->t2 = t2; 715 } 716 lease_p->preferred_lifetime = preferred_lifetime; 717 lease_p->valid_lifetime = valid_lifetime; 718 } 719 client->saved_verified = TRUE; 720 return; 721} 722 723STATIC DHCPv6PacketRef 724DHCPv6ClientMakePacket(DHCPv6ClientRef client, int message_type, 725 char * buf, int buf_size, 726 DHCPv6OptionAreaRef oa_p) 727{ 728 uint16_t elapsed_time; 729 DHCPv6OptionErrorString err; 730 DHCPv6PacketRef pkt; 731 732 pkt = (DHCPv6PacketRef)buf; 733 DHCPv6PacketSetMessageType(pkt, message_type); 734 DHCPv6PacketSetTransactionID(pkt, client->transaction_id); 735 DHCPv6OptionAreaInit(oa_p, pkt->options, 736 buf_size - DHCPV6_PACKET_HEADER_LENGTH); 737 if (S_insert_duid(oa_p) == FALSE) { 738 return (NULL); 739 } 740 if (DHCPv6OptionAreaAddOptionRequestOption(oa_p, 741 DHCPv6RequestedOptions, 742 DHCPv6RequestedOptionsCount, 743 &err) == FALSE) { 744 my_log(LOG_NOTICE, "DHCPv6Client: failed to add ORO, %s", 745 err.str); 746 return (NULL); 747 } 748 elapsed_time = get_elapsed_time(client); 749 if (DHCPv6OptionAreaAddOption(oa_p, kDHCPv6OPTION_ELAPSED_TIME, 750 sizeof(elapsed_time), &elapsed_time, 751 &err) == FALSE) { 752 my_log(LOG_NOTICE, "DHCPv6Client: failed to add ELAPSED_TIME, %s", 753 err.str); 754 return (NULL); 755 } 756 return (pkt); 757} 758 759 760STATIC void 761DHCPv6ClientSendInform(DHCPv6ClientRef client) 762{ 763 char buf[1500]; 764 int error; 765 interface_t * if_p = DHCPv6ClientGetInterface(client); 766 DHCPv6OptionArea oa; 767 DHCPv6PacketRef pkt; 768 769 pkt = DHCPv6ClientMakePacket(client, kDHCPv6MessageINFORMATION_REQUEST, 770 buf, sizeof(buf), &oa); 771 if (pkt == NULL) { 772 return; 773 } 774 error = DHCPv6SocketTransmit(client->sock, pkt, 775 DHCPV6_PACKET_HEADER_LENGTH 776 + DHCPv6OptionAreaGetUsedLength(&oa)); 777 switch (error) { 778 case 0: 779 case ENXIO: 780 case ENETDOWN: 781 break; 782 default: 783 my_log(LOG_NOTICE, "DHCPv6 %s: SendInformRequest transmit failed, %s", 784 if_name(if_p), strerror(error)); 785 break; 786 } 787 return; 788} 789 790STATIC void 791DHCPv6ClientSendSolicit(DHCPv6ClientRef client) 792{ 793 char buf[1500]; 794 DHCPv6OptionErrorString err; 795 int error; 796 DHCPv6OptionIA_NA ia_na; 797 interface_t * if_p = DHCPv6ClientGetInterface(client); 798 DHCPv6OptionArea oa; 799 DHCPv6PacketRef pkt; 800 801 pkt = DHCPv6ClientMakePacket(client, kDHCPv6MessageSOLICIT, 802 buf, sizeof(buf), &oa); 803 if (pkt == NULL) { 804 return; 805 } 806 DHCPv6OptionIA_NASetIAID(&ia_na, DHCPIAIDGet(if_name(if_p))); 807 DHCPv6OptionIA_NASetT1(&ia_na, 0); 808 DHCPv6OptionIA_NASetT2(&ia_na, 0); 809 if (DHCPv6OptionAreaAddOption(&oa, kDHCPv6OPTION_IA_NA, 810 DHCPv6OptionIA_NA_MIN_LENGTH, 811 &ia_na, &err) == FALSE) { 812 my_log(LOG_NOTICE, "DHCPv6Client: failed to add IA_NA, %s", 813 err.str); 814 return; 815 } 816 error = DHCPv6SocketTransmit(client->sock, pkt, 817 DHCPV6_PACKET_HEADER_LENGTH 818 + DHCPv6OptionAreaGetUsedLength(&oa)); 819 switch (error) { 820 case 0: 821 case ENXIO: 822 case ENETDOWN: 823 break; 824 default: 825 my_log(LOG_NOTICE, "DHCPv6 %s: SendSolicit transmit failed, %s", 826 if_name(if_p), strerror(error)); 827 break; 828 } 829 return; 830} 831 832STATIC void 833DHCPv6ClientSendPacket(DHCPv6ClientRef client) 834{ 835 char buf[1500]; 836 DHCPv6OptionErrorString err; 837 int error; 838 interface_t * if_p = DHCPv6ClientGetInterface(client); 839 int message_type; 840 DHCPv6OptionArea oa; 841 DHCPv6PacketRef pkt; 842 843 if (client->ia_na == NULL || client->server_id == NULL) { 844 my_log(LOG_NOTICE, "DHCPv6 %s: SendPacket given NULLs", 845 if_name(if_p)); 846 return; 847 } 848 switch (client->cstate) { 849 case kDHCPv6ClientStateRequest: 850 message_type = kDHCPv6MessageREQUEST; 851 break; 852 case kDHCPv6ClientStateRenew: 853 message_type = kDHCPv6MessageRENEW; 854 break; 855 case kDHCPv6ClientStateRebind: 856 message_type = kDHCPv6MessageREBIND; 857 break; 858 case kDHCPv6ClientStateRelease: 859 message_type = kDHCPv6MessageRELEASE; 860 break; 861 case kDHCPv6ClientStateConfirm: 862 message_type = kDHCPv6MessageCONFIRM; 863 break; 864 case kDHCPv6ClientStateDecline: 865 message_type = kDHCPv6MessageDECLINE; 866 break; 867 default: 868 my_log(LOG_NOTICE, 869 "DHCP %s: SendPacket doesn't know %s", 870 if_name(if_p), DHCPv6ClientStateGetName(client->cstate)); 871 return; 872 } 873 pkt = DHCPv6ClientMakePacket(client, message_type, 874 buf, sizeof(buf), &oa); 875 if (pkt == NULL) { 876 return; 877 } 878 switch (message_type) { 879 case kDHCPv6MessageREBIND: 880 case kDHCPv6MessageCONFIRM: 881 break; 882 default: 883 if (DHCPv6OptionAreaAddOption(&oa, kDHCPv6OPTION_SERVERID, 884 option_data_get_length(client->server_id), 885 client->server_id, &err) == FALSE) { 886 my_log(LOG_NOTICE, "DHCPv6Client: %s failed to add SERVERID, %s", 887 DHCPv6ClientStateGetName(client->cstate), err.str); 888 return; 889 } 890 break; 891 } 892 if (add_ia_na_option(client, &oa, &err) == FALSE) { 893 my_log(LOG_NOTICE, "DHCPv6Client: failed to add IA_NA, %s", 894 err.str); 895 return; 896 } 897 error = DHCPv6SocketTransmit(client->sock, pkt, 898 DHCPV6_PACKET_HEADER_LENGTH 899 + DHCPv6OptionAreaGetUsedLength(&oa)); 900 switch (error) { 901 case 0: 902 case ENXIO: 903 case ENETDOWN: 904 break; 905 default: 906 my_log(LOG_NOTICE, "DHCPv6 %s: SendPacket transmit failed, %s", 907 if_name(if_p), strerror(error)); 908 break; 909 } 910 return; 911} 912 913STATIC void 914DHCPv6Client_Inform(DHCPv6ClientRef client, IFEventID_t event_id, 915 void * event_data) 916{ 917 interface_t * if_p = DHCPv6ClientGetInterface(client); 918 919 switch (event_id) { 920 case IFEventID_start_e: 921 DHCPv6ClientSetState(client, kDHCPv6ClientStateInform); 922 DHCPv6ClientClearPacket(client); 923 DHCPv6ClientClearRetransmit(client); 924 client->transaction_id = get_new_transaction_id(); 925 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 926 DHCPv6Client_Inform, 927 client, (void *)IFEventID_data_e); 928 timer_callout_set(client->timer, 929 random_double_in_range(0, DHCPv6_INF_MAX_DELAY), 930 (timer_func_t *)DHCPv6Client_Inform, client, 931 (void *)IFEventID_timeout_e, NULL); 932 break; 933 case IFEventID_timeout_e: 934 if (client->try == 0) { 935 client->start_time = timer_get_current_time(); 936 } 937 else { 938 link_status_t link_status; 939 940 link_status = if_get_link_status(if_p); 941 if (link_status.valid && !link_status.active) { 942 DHCPv6ClientInactive(client); 943 break; 944 } 945 } 946 timer_callout_set(client->timer, 947 DHCPv6ClientNextRetransmit(client, 948 DHCPv6_INF_TIMEOUT, 949 DHCPv6_INF_MAX_RT), 950 (timer_func_t *)DHCPv6Client_Inform, 951 client, (void *)IFEventID_timeout_e, NULL); 952 my_log(LOG_DEBUG, "DHCPv6 %s: Inform Transmit (try=%d)", 953 if_name(if_p), client->try); 954 DHCPv6ClientSendInform(client); 955 break; 956 case IFEventID_data_e: { 957 DHCPv6SocketReceiveDataRef data; 958 959 data = (DHCPv6SocketReceiveDataRef)event_data; 960 if (data->pkt->msg_type != kDHCPv6MessageREPLY 961 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 962 != client->transaction_id) 963 || (S_duid_matches(data->options) == FALSE)) { 964 /* not a match */ 965 break; 966 } 967 my_log(LOG_DEBUG, "DHCPv6 %s: Reply Received (try=%d)", 968 if_name(if_p), client->try); 969 DHCPv6ClientSavePacket(client, data); 970 DHCPv6ClientPostNotification(client); 971 DHCPv6ClientCancelPendingEvents(client); 972 /* XXX don't necessarily take the first response */ 973 break; 974 } 975 default: 976 break; 977 } 978 return; 979} 980 981STATIC void 982DHCPv6Client_Release(DHCPv6ClientRef client, IFEventID_t event_id, 983 void * event_data) 984{ 985 interface_t * if_p = DHCPv6ClientGetInterface(client); 986 987 switch (event_id) { 988 case IFEventID_start_e: 989 DHCPv6ClientSetState(client, kDHCPv6ClientStateRelease); 990 DHCPv6ClientRemoveAddress(client, "Release"); 991 DHCPv6ClientCancelPendingEvents(client); 992 DHCPv6ClientClearRetransmit(client); 993 client->transaction_id = get_new_transaction_id(); 994 my_log(LOG_DEBUG, "DHCPv6 %s: Release Transmit", 995 if_name(if_p)); 996 DHCPv6ClientSendPacket(client); 997 /* 998 * We're supposed to wait for a Reply. Unfortunately, that's not 999 * possible because the code that invokes us expects the Stop 1000 * event to be synchronous. 1001 */ 1002 break; 1003 default: 1004 break; 1005 } 1006 return; 1007} 1008 1009STATIC void 1010DHCPv6Client_Decline(DHCPv6ClientRef client, IFEventID_t event_id, 1011 void * event_data) 1012{ 1013 interface_t * if_p = DHCPv6ClientGetInterface(client); 1014 1015 switch (event_id) { 1016 case IFEventID_start_e: 1017 DHCPv6ClientSetState(client, kDHCPv6ClientStateDecline); 1018 DHCPv6ClientRemoveAddress(client, "Decline"); 1019 DHCPv6ClientCancelPendingEvents(client); 1020 client->lease.valid = FALSE; 1021 client->saved_verified = FALSE; 1022 DHCPv6ClientPostNotification(client); 1023 DHCPv6ClientClearRetransmit(client); 1024 client->transaction_id = get_new_transaction_id(); 1025 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 1026 DHCPv6Client_Decline, 1027 client, (void *)IFEventID_data_e); 1028 /* FALL THROUGH */ 1029 case IFEventID_timeout_e: 1030 if (client->try >= DHCPv6_DEC_MAX_RC) { 1031 /* go back to Solicit */ 1032 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1033 return; 1034 } 1035 timer_callout_set(client->timer, 1036 DHCPv6ClientNextRetransmit(client, 1037 DHCPv6_DEC_TIMEOUT, 1038 0), 1039 (timer_func_t *)DHCPv6Client_Decline, 1040 client, (void *)IFEventID_timeout_e, NULL); 1041 my_log(LOG_DEBUG, "DHCPv6 %s: Decline Transmit (try=%d)", 1042 if_name(if_p), client->try); 1043 DHCPv6ClientSendPacket(client); 1044 break; 1045 1046 case IFEventID_data_e: { 1047 DHCPv6SocketReceiveDataRef data; 1048 int option_len; 1049 DHCPDUIDRef server_id; 1050 1051 data = (DHCPv6SocketReceiveDataRef)event_data; 1052 if (data->pkt->msg_type != kDHCPv6MessageREPLY 1053 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 1054 != client->transaction_id) 1055 || (S_duid_matches(data->options) == FALSE)) { 1056 /* not a match */ 1057 break; 1058 } 1059 server_id = (DHCPDUIDRef) 1060 DHCPv6OptionListGetOptionDataAndLength(data->options, 1061 kDHCPv6OPTION_SERVERID, 1062 &option_len, NULL); 1063 if (server_id == NULL 1064 || DHCPDUIDIsValid(server_id, option_len) == FALSE) { 1065 /* missing/invalid DUID */ 1066 break; 1067 } 1068 if (G_IPConfiguration_verbose) { 1069 my_log(LOG_DEBUG, "DHCPv6 %s: Reply Received (try=%d)", 1070 if_name(if_p), client->try); 1071 } 1072 /* back to Solicit */ 1073 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1074 break; 1075 } 1076 default: 1077 break; 1078 } 1079 return; 1080} 1081 1082STATIC void 1083DHCPv6Client_RenewRebind(DHCPv6ClientRef client, IFEventID_t event_id, 1084 void * event_data) 1085{ 1086 CFAbsoluteTime current_time = timer_get_current_time(); 1087 interface_t * if_p = DHCPv6ClientGetInterface(client); 1088 1089 switch (event_id) { 1090 case IFEventID_start_e: 1091 DHCPv6ClientSetState(client, kDHCPv6ClientStateRenew); 1092 DHCPv6ClientCancelPendingEvents(client); 1093 DHCPv6ClientClearRetransmit(client); 1094 client->start_time = current_time; 1095 client->transaction_id = get_new_transaction_id(); 1096 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 1097 DHCPv6Client_RenewRebind, 1098 client, (void *)IFEventID_data_e); 1099 /* FALL THROUGH */ 1100 case IFEventID_timeout_e: { 1101 CFTimeInterval time_since_start; 1102 CFTimeInterval wait_time; 1103 1104 if (current_time < client->lease.start) { 1105 /* time went backwards? */ 1106 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1107 return; 1108 } 1109 time_since_start = current_time - client->lease.start; 1110 if (time_since_start >= client->lease.valid_lifetime) { 1111 /* expired */ 1112 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1113 return; 1114 } 1115 1116 if (time_since_start < client->lease.t2) { 1117 CFTimeInterval time_until_t2; 1118 1119 /* Renew (before T2) */ 1120 wait_time = DHCPv6ClientNextRetransmit(client, 1121 DHCPv6_REN_TIMEOUT, 1122 DHCPv6_REN_MAX_RT); 1123 time_until_t2 = client->lease.t2 - time_since_start; 1124 if (wait_time > time_until_t2) { 1125 wait_time = time_until_t2; 1126 } 1127 } 1128 else { 1129 CFTimeInterval time_until_expiration; 1130 1131 /* Rebind (T2 or later) */ 1132 if (client->cstate != kDHCPv6ClientStateRebind) { 1133 /* switch to Rebind */ 1134 DHCPv6ClientSetState(client, kDHCPv6ClientStateRebind); 1135 DHCPv6ClientClearRetransmit(client); 1136 } 1137 wait_time = DHCPv6ClientNextRetransmit(client, 1138 DHCPv6_REB_TIMEOUT, 1139 DHCPv6_REB_MAX_RT); 1140 time_until_expiration 1141 = client->lease.valid_lifetime - time_since_start; 1142 if (wait_time > time_until_expiration) { 1143 wait_time = time_until_expiration; 1144 } 1145 } 1146 timer_callout_set(client->timer, 1147 wait_time, 1148 (timer_func_t *)DHCPv6Client_RenewRebind, 1149 client, (void *)IFEventID_timeout_e, NULL); 1150 my_log(LOG_DEBUG, "DHCPv6 %s: %s Transmit (try=%d)", 1151 if_name(if_p), DHCPv6ClientStateGetName(client->cstate), 1152 client->try); 1153 DHCPv6ClientSendPacket(client); 1154 break; 1155 } 1156 case IFEventID_data_e: { 1157 DHCPv6SocketReceiveDataRef data; 1158 DHCPv6OptionIA_NARef ia_na; 1159 DHCPv6OptionIAADDRRef ia_addr; 1160 char ntopbuf[INET6_ADDRSTRLEN]; 1161 int option_len; 1162 DHCPDUIDRef server_id; 1163 DHCPv6OptionSTATUS_CODERef status_code; 1164 1165 data = (DHCPv6SocketReceiveDataRef)event_data; 1166 if (data->pkt->msg_type != kDHCPv6MessageREPLY 1167 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 1168 != client->transaction_id) 1169 || (S_duid_matches(data->options) == FALSE)) { 1170 /* not a match */ 1171 break; 1172 } 1173 server_id = (DHCPDUIDRef) 1174 DHCPv6OptionListGetOptionDataAndLength(data->options, 1175 kDHCPv6OPTION_SERVERID, 1176 &option_len, NULL); 1177 if (server_id == NULL 1178 || DHCPDUIDIsValid(server_id, option_len) == FALSE) { 1179 /* missing/invalid DUID */ 1180 break; 1181 } 1182 status_code = (DHCPv6OptionSTATUS_CODERef) 1183 DHCPv6OptionListGetOptionDataAndLength(data->options, 1184 kDHCPv6OPTION_STATUS_CODE, 1185 &option_len, NULL); 1186 if (status_code != NULL) { 1187 uint16_t code; 1188 1189 if (option_len < DHCPv6OptionSTATUS_CODE_MIN_LENGTH) { 1190 /* too short */ 1191 break; 1192 } 1193 1194 code = DHCPv6OptionSTATUS_CODEGetCode(status_code); 1195 if (code != kDHCPv6StatusCodeSuccess) { 1196 /* XXX check for a specific value maybe? */ 1197 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1198 return; 1199 } 1200 } 1201 ia_na = get_ia_na_addr(client, data->pkt->msg_type, 1202 data->options, &ia_addr); 1203 if (ia_na == NULL) { 1204 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1205 break; 1206 } 1207 if (G_IPConfiguration_verbose) { 1208 my_log(LOG_DEBUG, "DHCPv6 %s: %s Received Reply (try=%d) " 1209 "IAADDR %s Preferred %d Valid=%d", 1210 if_name(if_p), 1211 DHCPv6ClientStateGetName(client->cstate), 1212 client->try, 1213 inet_ntop(AF_INET6, 1214 DHCPv6OptionIAADDRGetAddress(ia_addr), 1215 ntopbuf, sizeof(ntopbuf)), 1216 DHCPv6OptionIAADDRGetPreferredLifetime(ia_addr), 1217 DHCPv6OptionIAADDRGetValidLifetime(ia_addr)); 1218 } 1219 DHCPv6ClientSavePacket(client, data); 1220 DHCPv6Client_Bound(client, IFEventID_start_e, NULL); 1221 break; 1222 } 1223 default: 1224 break; 1225 } 1226} 1227 1228STATIC void 1229DHCPv6Client_Confirm(DHCPv6ClientRef client, IFEventID_t event_id, 1230 void * event_data) 1231{ 1232 CFAbsoluteTime current_time = timer_get_current_time(); 1233 interface_t * if_p = DHCPv6ClientGetInterface(client); 1234 1235 switch (event_id) { 1236 case IFEventID_start_e: 1237 DHCPv6ClientSetState(client, kDHCPv6ClientStateConfirm); 1238 DHCPv6ClientCancelPendingEvents(client); 1239 DHCPv6ClientClearRetransmit(client); 1240 client->saved_verified = FALSE; 1241 client->transaction_id = get_new_transaction_id(); 1242 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 1243 DHCPv6Client_Confirm, 1244 client, (void *)IFEventID_data_e); 1245 timer_callout_set(client->timer, 1246 random_double_in_range(0, DHCPv6_CNF_MAX_DELAY), 1247 (timer_func_t *)DHCPv6Client_Confirm, client, 1248 (void *)IFEventID_timeout_e, NULL); 1249 break; 1250 case IFEventID_timeout_e: 1251 if (client->try == 0) { 1252 client->start_time = current_time; 1253 } 1254 else { 1255 bool done = FALSE; 1256 link_status_t link_status; 1257 1258 link_status = if_get_link_status(if_p); 1259 if (link_status.valid && !link_status.active) { 1260 DHCPv6ClientInactive(client); 1261 break; 1262 } 1263 if (current_time > client->start_time) { 1264 if ((current_time - client->start_time) >= DHCPv6_CNF_MAX_RD) { 1265 done = TRUE; 1266 } 1267 } 1268 else { 1269 done = TRUE; 1270 } 1271 if (done) { 1272 if (DHCPv6ClientLeaseStillValid(client)) { 1273 DHCPv6Client_Bound(client, IFEventID_start_e, NULL); 1274 return; 1275 } 1276 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1277 return; 1278 } 1279 } 1280 timer_callout_set(client->timer, 1281 DHCPv6ClientNextRetransmit(client, 1282 DHCPv6_CNF_TIMEOUT, 1283 DHCPv6_CNF_MAX_RT), 1284 (timer_func_t *)DHCPv6Client_Confirm, 1285 client, (void *)IFEventID_timeout_e, NULL); 1286 my_log(LOG_DEBUG, "DHCPv6 %s: Confirm Transmit (try=%d)", 1287 if_name(if_p), client->try); 1288 DHCPv6ClientSendPacket(client); 1289 break; 1290 case IFEventID_data_e: { 1291 DHCPv6SocketReceiveDataRef data; 1292 int option_len; 1293 DHCPDUIDRef server_id; 1294 DHCPv6OptionSTATUS_CODERef status_code; 1295 1296 data = (DHCPv6SocketReceiveDataRef)event_data; 1297 if (data->pkt->msg_type != kDHCPv6MessageREPLY 1298 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 1299 != client->transaction_id) 1300 || (S_duid_matches(data->options) == FALSE)) { 1301 /* not a match */ 1302 break; 1303 } 1304 server_id = (DHCPDUIDRef) 1305 DHCPv6OptionListGetOptionDataAndLength(data->options, 1306 kDHCPv6OPTION_SERVERID, 1307 &option_len, NULL); 1308 if (server_id == NULL 1309 || DHCPDUIDIsValid(server_id, option_len) == FALSE) { 1310 /* missing/invalid DUID */ 1311 break; 1312 } 1313 status_code = (DHCPv6OptionSTATUS_CODERef) 1314 DHCPv6OptionListGetOptionDataAndLength(data->options, 1315 kDHCPv6OPTION_STATUS_CODE, 1316 &option_len, NULL); 1317 if (status_code == NULL 1318 || option_len < DHCPv6OptionSTATUS_CODE_MIN_LENGTH) { 1319 break; 1320 } 1321 if (DHCPv6OptionSTATUS_CODEGetCode(status_code) 1322 != kDHCPv6StatusCodeSuccess) { 1323 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1324 return; 1325 } 1326 if (G_IPConfiguration_verbose) { 1327 my_log(LOG_DEBUG, "DHCPv6 %s: Reply Received (try=%d)", 1328 if_name(if_p), client->try); 1329 } 1330 DHCPv6Client_Bound(client, IFEventID_start_e, NULL); 1331 break; 1332 } 1333 default: 1334 break; 1335 } 1336} 1337 1338STATIC void 1339DHCPv6ClientHandleAddressChanged(DHCPv6ClientRef client, 1340 inet6_addrlist_t * addr_list_p) 1341{ 1342 int i; 1343 inet6_addrinfo_t * scan; 1344 1345 if (addr_list_p == NULL || addr_list_p->count == 0) { 1346 /* no addresses configured, nothing to do */ 1347 return; 1348 } 1349 for (i = 0, scan = addr_list_p->list; 1350 i < addr_list_p->count; i++, scan++) { 1351 if (IN6_ARE_ADDR_EQUAL(&client->our_ip, &scan->addr)) { 1352 /* someone else is using this address, decline it */ 1353 if ((scan->addr_flags & IN6_IFF_DUPLICATED) != 0) { 1354 DHCPv6Client_Decline(client, IFEventID_start_e, NULL); 1355 return; 1356 } 1357 if ((scan->addr_flags & IN6_IFF_TENTATIVE) != 0) { 1358 my_log(LOG_DEBUG, "address is still tentative"); 1359 /* address is still tentative */ 1360 break; 1361 } 1362 /* notify that we're ready */ 1363 DHCPv6ClientPostNotification(client); 1364 DHCPv6ClientCancelPendingEvents(client); 1365 1366 /* set a timer to start in Renew */ 1367 if (client->lease.valid_lifetime != DHCP_INFINITE_LEASE) { 1368 CFAbsoluteTime current_time = timer_get_current_time(); 1369 uint32_t t1 = client->lease.t1; 1370 CFTimeInterval time_since_start = 0; 1371 1372 if (current_time < client->lease.start) { 1373 /* time went backwards? */ 1374 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1375 return; 1376 } 1377 time_since_start = current_time - client->lease.start; 1378 if (time_since_start < t1) { 1379 t1 -= time_since_start; 1380 } 1381 else { 1382 t1 = 10; /* wakeup in 10 seconds */ 1383 } 1384 timer_callout_set(client->timer, t1, 1385 (timer_func_t *)DHCPv6Client_RenewRebind, 1386 client, (void *)IFEventID_start_e, NULL); 1387 } 1388 break; 1389 } 1390 } 1391 return; 1392} 1393 1394STATIC void 1395DHCPv6ClientSimulateAddressChanged(DHCPv6ClientRef client) 1396{ 1397 inet6_addrlist_t addr_list; 1398 interface_t * if_p = DHCPv6ClientGetInterface(client); 1399 1400 inet6_addrlist_copy(&addr_list, if_link_index(if_p)); 1401 DHCPv6ClientHandleAddressChanged(client, &addr_list); 1402 inet6_addrlist_free(&addr_list); 1403 return; 1404} 1405 1406STATIC void 1407DHCPv6Client_Bound(DHCPv6ClientRef client, IFEventID_t event_id, 1408 void * event_data) 1409{ 1410 inet6_addrlist_t * addr_list_p; 1411 interface_t * if_p = DHCPv6ClientGetInterface(client); 1412 char ntopbuf[INET6_ADDRSTRLEN]; 1413 1414 switch (event_id) { 1415 case IFEventID_start_e: { 1416 struct in6_addr * our_ip; 1417 struct in6_addr our_ip_aligned; 1418 uint32_t preferred_lifetime; 1419 int prefix_length; 1420 int s; 1421 bool same_address = FALSE; 1422 CFTimeInterval time_since_start = 0; 1423 uint32_t valid_lifetime; 1424 1425 our_ip = &our_ip_aligned; 1426 bcopy((void *)DHCPv6OptionIAADDRGetAddress(client->ia_addr), 1427 our_ip, sizeof(our_ip_aligned)); 1428 1429 DHCPv6ClientSetState(client, kDHCPv6ClientStateBound); 1430 client->lease.valid = TRUE; 1431 client->saved_verified = TRUE; 1432 DHCPv6ClientCancelPendingEvents(client); 1433 1434 valid_lifetime = client->lease.valid_lifetime; 1435 preferred_lifetime = client->lease.preferred_lifetime; 1436 if (valid_lifetime != DHCP_INFINITE_LEASE) { 1437 CFAbsoluteTime current_time = timer_get_current_time(); 1438 1439 if (current_time < client->lease.start) { 1440 /* time went backwards? */ 1441 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1442 return; 1443 } 1444 time_since_start = current_time - client->lease.start; 1445 if (time_since_start >= client->lease.valid_lifetime) { 1446 /* expired */ 1447 DHCPv6Client_Unbound(client, IFEventID_start_e, NULL); 1448 return; 1449 } 1450 /* reduce the time left by the amount that's elapsed already */ 1451 valid_lifetime -= time_since_start; 1452 if (time_since_start < preferred_lifetime) { 1453 preferred_lifetime -= time_since_start; 1454 } 1455 else { 1456 preferred_lifetime = 0; /* XXX really? */ 1457 } 1458 } 1459 s = inet6_dgram_socket(); 1460 if (s < 0) { 1461 my_log(LOG_ERR, 1462 "DHCPv6ClientBound(%s):" 1463 " socket() failed, %s (%d)", 1464 if_name(if_p), strerror(errno), errno); 1465 break; 1466 } 1467 /* if the address has changed, remove the old first */ 1468 if (IN6_IS_ADDR_UNSPECIFIED(&client->our_ip) == FALSE) { 1469 if (IN6_ARE_ADDR_EQUAL(&client->our_ip, our_ip)) { 1470 same_address = TRUE; 1471 } 1472 else { 1473 if (G_IPConfiguration_verbose) { 1474 my_log(LOG_DEBUG, "DHCPv6 %s: Bound: removing %s", 1475 if_name(if_p), 1476 inet_ntop(AF_INET6, &client->our_ip, 1477 ntopbuf, sizeof(ntopbuf))); 1478 } 1479 if (inet6_difaddr(s, if_name(if_p), &client->our_ip) < 0) { 1480 my_log(LOG_DEBUG, 1481 "DHCPv6ClientBound(%s): remove %s failed, %s (%d)", 1482 if_name(if_p), 1483 inet_ntop(AF_INET6, &client->our_ip, 1484 ntopbuf, sizeof(ntopbuf)), 1485 strerror(errno), errno); 1486 } 1487 } 1488 } 1489 prefix_length = S_get_prefix_length(our_ip, if_link_index(if_p)); 1490 if (G_IPConfiguration_verbose) { 1491 my_log(LOG_DEBUG, 1492 "DHCPv6 %s: setting %s/%d valid %d preferred %d", 1493 if_name(if_p), 1494 inet_ntop(AF_INET6, our_ip, ntopbuf, sizeof(ntopbuf)), 1495 prefix_length, valid_lifetime, preferred_lifetime); 1496 } 1497 if (inet6_aifaddr(s, if_name(if_p), our_ip, NULL, 1498 prefix_length, 1499 valid_lifetime, preferred_lifetime) < 0) { 1500 my_log(LOG_DEBUG, 1501 "DHCPv6ClientBound(%s): adding %s failed, %s (%d)", 1502 if_name(if_p), 1503 inet_ntop(AF_INET6, our_ip, 1504 ntopbuf, sizeof(ntopbuf)), 1505 strerror(errno), errno); 1506 } 1507 else if (same_address) { 1508 /* notify that we're ready */ 1509 DHCPv6ClientPostNotification(client); 1510 DHCPv6ClientCancelPendingEvents(client); 1511 1512 /* set a timer to start in Renew */ 1513 if (client->lease.valid_lifetime != DHCP_INFINITE_LEASE) { 1514 uint32_t t1 = client->lease.t1; 1515 1516 if (time_since_start < t1) { 1517 t1 -= time_since_start; 1518 } 1519 else { 1520 t1 = 10; /* wakeup in 10 seconds */ 1521 1522 } 1523 timer_callout_set(client->timer, t1, 1524 (timer_func_t *)DHCPv6Client_RenewRebind, 1525 client, (void *)IFEventID_start_e, NULL); 1526 } 1527 } 1528 else { 1529 /* register to receive address changed notifications */ 1530 DHCPv6ClientSetAddressChangedFunc(client, DHCPv6Client_Bound); 1531 client->our_ip = *our_ip; 1532 client->our_prefix_length = prefix_length; 1533 /* and see what addresses are there now */ 1534 DHCPv6ClientSimulateAddressChanged(client); 1535 } 1536 close(s); 1537 break; 1538 } 1539 case IFEventID_ipv6_address_changed_e: 1540 addr_list_p = (inet6_addrlist_t *)event_data; 1541 DHCPv6ClientHandleAddressChanged(client, addr_list_p); 1542 break; 1543 default: 1544 break; 1545 } 1546} 1547 1548STATIC void 1549DHCPv6Client_Unbound(DHCPv6ClientRef client, IFEventID_t event_id, 1550 void * event_data) 1551{ 1552 switch (event_id) { 1553 case IFEventID_start_e: 1554 DHCPv6ClientSetState(client, kDHCPv6ClientStateUnbound); 1555 DHCPv6ClientCancelPendingEvents(client); 1556 DHCPv6ClientRemoveAddress(client, "Unbound"); 1557 DHCPv6ClientClearPacket(client); 1558 DHCPv6ClientPostNotification(client); 1559 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1560 break; 1561 default: 1562 break; 1563 } 1564} 1565 1566STATIC void 1567DHCPv6Client_Request(DHCPv6ClientRef client, IFEventID_t event_id, 1568 void * event_data) 1569{ 1570 interface_t * if_p = DHCPv6ClientGetInterface(client); 1571 1572 switch (event_id) { 1573 case IFEventID_start_e: 1574 DHCPv6ClientSetState(client, kDHCPv6ClientStateRequest); 1575 DHCPv6ClientClearRetransmit(client); 1576 client->transaction_id = get_new_transaction_id(); 1577 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 1578 DHCPv6Client_Request, 1579 client, (void *)IFEventID_data_e); 1580 /* FALL THROUGH */ 1581 case IFEventID_timeout_e: { 1582 if (client->try >= DHCPv6_REQ_MAX_RC) { 1583 /* go back to Solicit */ 1584 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1585 return; 1586 } 1587 timer_callout_set(client->timer, 1588 DHCPv6ClientNextRetransmit(client, 1589 DHCPv6_REQ_TIMEOUT, 1590 DHCPv6_REQ_MAX_RT), 1591 (timer_func_t *)DHCPv6Client_Request, 1592 client, (void *)IFEventID_timeout_e, NULL); 1593 my_log(LOG_DEBUG, "DHCPv6 %s: Request Transmit (try=%d)", 1594 if_name(if_p), client->try); 1595 DHCPv6ClientSendPacket(client); 1596 break; 1597 } 1598 case IFEventID_data_e: { 1599 DHCPv6SocketReceiveDataRef data; 1600 DHCPv6OptionIA_NARef ia_na; 1601 DHCPv6OptionIAADDRRef ia_addr; 1602 char ntopbuf[INET6_ADDRSTRLEN]; 1603 int option_len; 1604 DHCPDUIDRef server_id; 1605 DHCPv6OptionSTATUS_CODERef status_code; 1606 1607 data = (DHCPv6SocketReceiveDataRef)event_data; 1608 if (data->pkt->msg_type != kDHCPv6MessageREPLY 1609 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 1610 != client->transaction_id) 1611 || (S_duid_matches(data->options) == FALSE)) { 1612 /* not a match */ 1613 break; 1614 } 1615 server_id = (DHCPDUIDRef) 1616 DHCPv6OptionListGetOptionDataAndLength(data->options, 1617 kDHCPv6OPTION_SERVERID, 1618 &option_len, NULL); 1619 if (server_id == NULL 1620 || DHCPDUIDIsValid(server_id, option_len) == FALSE) { 1621 /* missing/invalid DUID */ 1622 break; 1623 } 1624 status_code = (DHCPv6OptionSTATUS_CODERef) 1625 DHCPv6OptionListGetOptionDataAndLength(data->options, 1626 kDHCPv6OPTION_STATUS_CODE, 1627 &option_len, NULL); 1628 if (status_code != NULL) { 1629 uint16_t code; 1630 1631 if (option_len < DHCPv6OptionSTATUS_CODE_MIN_LENGTH) { 1632 /* too short */ 1633 break; 1634 } 1635 1636 code = DHCPv6OptionSTATUS_CODEGetCode(status_code); 1637 if (code != kDHCPv6StatusCodeSuccess) { 1638 if (code == kDHCPv6StatusCodeNoAddrsAvail) { 1639 /* must ignore it */ 1640 break; 1641 } 1642 } 1643 } 1644 ia_na = get_ia_na_addr(client, data->pkt->msg_type, 1645 data->options, &ia_addr); 1646 if (ia_na == NULL) { 1647 break; 1648 } 1649 if (G_IPConfiguration_verbose) { 1650 my_log(LOG_DEBUG, "DHCPv6 %s: Reply Received (try=%d) " 1651 "IAADDR %s Preferred %d Valid=%d", 1652 if_name(if_p), 1653 client->try, 1654 inet_ntop(AF_INET6, 1655 DHCPv6OptionIAADDRGetAddress(ia_addr), 1656 ntopbuf, sizeof(ntopbuf)), 1657 DHCPv6OptionIAADDRGetPreferredLifetime(ia_addr), 1658 DHCPv6OptionIAADDRGetValidLifetime(ia_addr)); 1659 } 1660 DHCPv6ClientSavePacket(client, data); 1661 DHCPv6Client_Bound(client, IFEventID_start_e, NULL); 1662 break; 1663 } 1664 default: 1665 break; 1666 } 1667 return; 1668} 1669 1670STATIC void 1671DHCPv6Client_Solicit(DHCPv6ClientRef client, IFEventID_t event_id, 1672 void * event_data) 1673{ 1674 interface_t * if_p = DHCPv6ClientGetInterface(client); 1675 1676 switch (event_id) { 1677 case IFEventID_start_e: 1678 DHCPv6ClientSetState(client, kDHCPv6ClientStateSolicit); 1679 DHCPv6ClientClearRetransmit(client); 1680 DHCPv6ClientClearPacket(client); 1681 client->transaction_id = get_new_transaction_id(); 1682 DHCPv6SocketEnableReceive(client->sock, (DHCPv6SocketReceiveFuncPtr) 1683 DHCPv6Client_Solicit, 1684 client, (void *)IFEventID_data_e); 1685 timer_callout_set(client->timer, 1686 random_double_in_range(0, DHCPv6_SOL_MAX_DELAY), 1687 (timer_func_t *)DHCPv6Client_Solicit, client, 1688 (void *)IFEventID_timeout_e, NULL); 1689 break; 1690 case IFEventID_timeout_e: { 1691 if (client->try == 0) { 1692 client->start_time = timer_get_current_time(); 1693 } 1694 else { 1695 link_status_t link_status; 1696 1697 link_status = if_get_link_status(if_p); 1698 if (link_status.valid && !link_status.active) { 1699 DHCPv6ClientInactive(client); 1700 break; 1701 } 1702 } 1703 /* we've got a packet */ 1704 if (client->saved.pkt_len != 0) { 1705 DHCPv6Client_Request(client, IFEventID_start_e, NULL); 1706 return; 1707 } 1708 timer_callout_set(client->timer, 1709 DHCPv6ClientNextRetransmit(client, 1710 DHCPv6_SOL_TIMEOUT, 1711 DHCPv6_SOL_MAX_RT), 1712 (timer_func_t *)DHCPv6Client_Solicit, 1713 client, (void *)IFEventID_timeout_e, NULL); 1714 my_log(LOG_DEBUG, "DHCPv6 %s: Solicit Transmit (try=%d)", 1715 if_name(if_p), client->try); 1716 DHCPv6ClientSendSolicit(client); 1717 break; 1718 } 1719 case IFEventID_data_e: { 1720 DHCPv6SocketReceiveDataRef data; 1721 DHCPv6OptionIA_NARef ia_na; 1722 DHCPv6OptionIAADDRRef ia_addr; 1723 char ntopbuf[INET6_ADDRSTRLEN]; 1724 int option_len; 1725 uint8_t pref; 1726 DHCPDUIDRef server_id; 1727 DHCPv6OptionSTATUS_CODERef status_code; 1728 1729 data = (DHCPv6SocketReceiveDataRef)event_data; 1730 if (data->pkt->msg_type != kDHCPv6MessageADVERTISE 1731 || (DHCPv6PacketGetTransactionID((const DHCPv6PacketRef)data->pkt) 1732 != client->transaction_id) 1733 || (S_duid_matches(data->options) == FALSE)) { 1734 /* not a match */ 1735 break; 1736 } 1737 server_id = (DHCPDUIDRef) 1738 DHCPv6OptionListGetOptionDataAndLength(data->options, 1739 kDHCPv6OPTION_SERVERID, 1740 &option_len, NULL); 1741 if (server_id == NULL 1742 || DHCPDUIDIsValid(server_id, option_len) == FALSE) { 1743 /* missing/invalid DUID */ 1744 break; 1745 } 1746 status_code = (DHCPv6OptionSTATUS_CODERef) 1747 DHCPv6OptionListGetOptionDataAndLength(data->options, 1748 kDHCPv6OPTION_STATUS_CODE, 1749 &option_len, NULL); 1750 if (status_code != NULL) { 1751 uint16_t code; 1752 1753 if (option_len < DHCPv6OptionSTATUS_CODE_MIN_LENGTH) { 1754 /* too short */ 1755 break; 1756 } 1757 1758 code = DHCPv6OptionSTATUS_CODEGetCode(status_code); 1759 if (code != kDHCPv6StatusCodeSuccess) { 1760 if (code == kDHCPv6StatusCodeNoAddrsAvail) { 1761 /* must ignore it */ 1762 break; 1763 } 1764 } 1765 } 1766 ia_na = get_ia_na_addr(client, data->pkt->msg_type, 1767 data->options, &ia_addr); 1768 if (ia_na == NULL) { 1769 break; 1770 } 1771 if (G_IPConfiguration_verbose) { 1772 my_log(LOG_DEBUG, "DHCPv6 %s: Advertise Received (try=%d) " 1773 "IAADDR %s Preferred %d Valid=%d", 1774 if_name(if_p), 1775 client->try, 1776 inet_ntop(AF_INET6, 1777 DHCPv6OptionIAADDRGetAddress(ia_addr), 1778 ntopbuf, sizeof(ntopbuf)), 1779 DHCPv6OptionIAADDRGetPreferredLifetime(ia_addr), 1780 DHCPv6OptionIAADDRGetValidLifetime(ia_addr)); 1781 } 1782 1783 /* check for a server preference value */ 1784 pref = get_preference_value_from_options(data->options); 1785 1786 /* if this response is "better" than one we saved, use it */ 1787 if (client->saved.options != NULL) { 1788 uint8_t saved_pref; 1789 1790 saved_pref 1791 = get_preference_value_from_options(client->saved.options); 1792 if (saved_pref >= pref) { 1793 /* saved packet is still "better" */ 1794 break; 1795 } 1796 } 1797 if (G_IPConfiguration_verbose) { 1798 CFMutableStringRef str; 1799 1800 str = CFStringCreateMutable(NULL, 0); 1801 DHCPDUIDPrintToString(str, server_id, 1802 option_data_get_length(server_id)); 1803 my_log(LOG_DEBUG, "DHCPv6 %s: Saving Advertise from %@", 1804 if_name(if_p), str); 1805 CFRelease(str); 1806 } 1807 DHCPv6ClientSavePacket(client, data); 1808 if (pref == kDHCPv6OptionPREFERENCEMaxValue) { 1809 /* if preference is max, jump right to Request */ 1810 DHCPv6Client_Request(client, IFEventID_start_e, NULL); 1811 break; 1812 } 1813 break; 1814 } 1815 default: 1816 break; 1817 } 1818 return; 1819} 1820 1821DHCPv6ClientRef 1822DHCPv6ClientCreate(interface_t * if_p) 1823{ 1824 DHCPv6ClientRef client; 1825 1826 client = (DHCPv6ClientRef)malloc(sizeof(*client)); 1827 bzero(client, sizeof(*client)); 1828 client->sock = DHCPv6SocketCreate(if_p); 1829 client->timer = timer_callout_init(); 1830 return (client); 1831} 1832 1833void 1834DHCPv6ClientStart(DHCPv6ClientRef client, bool allocate_address) 1835{ 1836 if (allocate_address) { 1837 /* start Stateful */ 1838 if (DHCPv6ClientLeaseStillValid(client)) { 1839 DHCPv6Client_Confirm(client, IFEventID_start_e, NULL); 1840 } 1841 else { 1842 DHCPv6Client_Solicit(client, IFEventID_start_e, NULL); 1843 } 1844 } 1845 else { 1846 /* start Stateless */ 1847 DHCPv6ClientRemoveAddress(client, "Start"); 1848 DHCPv6Client_Inform(client, IFEventID_start_e, NULL); 1849 } 1850 return; 1851} 1852 1853void 1854DHCPv6ClientStop(DHCPv6ClientRef client, bool discard_information) 1855{ 1856 /* remove the IP address */ 1857 DHCPv6ClientRemoveAddress(client, "Stop"); 1858 DHCPv6ClientCancelPendingEvents(client); 1859 if (discard_information) { 1860 DHCPv6ClientClearPacket(client); 1861 } 1862 else { 1863 client->saved_verified = FALSE; 1864 } 1865 DHCPv6ClientPostNotification(client); 1866 return; 1867} 1868 1869void 1870DHCPv6ClientRelease(DHCPv6ClientRef * client_p) 1871{ 1872 DHCPv6ClientRef client = *client_p; 1873 1874 if (client == NULL) { 1875 return; 1876 } 1877 *client_p = NULL; 1878 if (DHCPv6ClientLeaseStillValid(client)) { 1879 DHCPv6Client_Release(client, IFEventID_start_e, NULL); 1880 } 1881 if (client->timer != NULL) { 1882 timer_callout_free(&client->timer); 1883 } 1884 DHCPv6SocketRelease(&client->sock); 1885 if (client->saved.pkt != NULL) { 1886 free(client->saved.pkt); 1887 client->saved.pkt = NULL; 1888 } 1889 DHCPv6ClientSetNotificationCallBack(client, NULL, NULL); 1890 DHCPv6OptionListRelease(&client->saved.options); 1891 free(client); 1892 return; 1893} 1894 1895bool 1896DHCPv6ClientGetInfo(DHCPv6ClientRef client, dhcpv6_info_t * info_p) 1897{ 1898 if (client->saved.options == NULL || client->saved_verified == FALSE) { 1899 info_p->pkt = NULL; 1900 info_p->pkt_len = 0; 1901 info_p->options = NULL; 1902 return (FALSE); 1903 } 1904 *info_p = client->saved; 1905 return (TRUE); 1906} 1907 1908void 1909DHCPv6ClientCopyAddresses(DHCPv6ClientRef client, 1910 inet6_addrlist_t * addr_list_p) 1911{ 1912 if (IN6_IS_ADDR_UNSPECIFIED(&client->our_ip)) { 1913 inet6_addrlist_init(addr_list_p); 1914 return; 1915 } 1916 addr_list_p->list = addr_list_p->list_static; 1917 addr_list_p->count = 1; 1918 addr_list_p->list[0].addr = client->our_ip; 1919 addr_list_p->list[0].prefix_length = client->our_prefix_length; 1920 addr_list_p->list[0].addr_flags = 0; 1921 return; 1922} 1923 1924STATIC void 1925DHCPv6ClientDeliverNotification(void * info) 1926{ 1927 DHCPv6ClientRef client = (DHCPv6ClientRef)info; 1928 1929 if (client->callback == NULL) { 1930 /* this can't really happen */ 1931 my_log(LOG_NOTICE, 1932 "DHCPv6Client: runloop source signaled but callback is NULL"); 1933 return; 1934 } 1935 (*client->callback)(client->callback_arg, client); 1936 return; 1937} 1938 1939void 1940DHCPv6ClientSetNotificationCallBack(DHCPv6ClientRef client, 1941 DHCPv6ClientNotificationCallBack callback, 1942 void * callback_arg) 1943{ 1944 client->callback = callback; 1945 client->callback_arg = callback_arg; 1946 if (callback == NULL) { 1947 if (client->callback_rls != NULL) { 1948 CFRunLoopSourceInvalidate(client->callback_rls); 1949 my_CFRelease(&client->callback_rls); 1950 } 1951 } 1952 else if (client->callback_rls == NULL) { 1953 CFRunLoopSourceContext context; 1954 1955 bzero(&context, sizeof(context)); 1956 context.info = (void *)client; 1957 context.perform = DHCPv6ClientDeliverNotification; 1958 client->callback_rls = CFRunLoopSourceCreate(NULL, 0, &context); 1959 CFRunLoopAddSource(CFRunLoopGetCurrent(), client->callback_rls, 1960 kCFRunLoopDefaultMode); 1961 } 1962 return; 1963} 1964 1965void 1966DHCPv6ClientAddressChanged(DHCPv6ClientRef client, 1967 inet6_addrlist_t * addr_list_p) 1968{ 1969 if (addr_list_p == NULL || addr_list_p->count == 0) { 1970 /* no addresses configured, nothing to do */ 1971 return; 1972 } 1973 if (client->address_changed_func != NULL) { 1974 (*client->address_changed_func)(client, 1975 IFEventID_ipv6_address_changed_e, 1976 addr_list_p); 1977 } 1978 else { 1979 /* XXX check for an on-going conflict */ 1980 } 1981 return; 1982} 1983 1984#if TEST_DHCPV6_CLIENT 1985#include "sysconfig.h" 1986 1987#include <SystemConfiguration/SCPrivate.h> 1988 1989boolean_t G_is_netboot; 1990int G_dhcp_duid_type; 1991Boolean G_IPConfiguration_verbose = TRUE; 1992 1993bool S_allocate_address; 1994 1995STATIC void 1996client_notification(void * callback_arg, DHCPv6ClientRef client) 1997{ 1998 dhcpv6_info_t info; 1999 2000 if (DHCPv6ClientGetInfo(client, &info) == FALSE) { 2001 printf("DHCPv6 updated: no info\n"); 2002 } 2003 else { 2004 printf("DHCPv6 updated\n"); 2005 DHCPv6OptionListFPrint(stdout, info.options); 2006 } 2007 return; 2008} 2009 2010interface_list_t * 2011get_interface_list(void) 2012{ 2013 STATIC interface_list_t * S_interfaces; 2014 2015 if (S_interfaces == NULL) { 2016 S_interfaces = ifl_init(); 2017 } 2018 return (S_interfaces); 2019} 2020 2021STATIC void 2022handle_change(SCDynamicStoreRef session, CFArrayRef changes, void * arg) 2023{ 2024 int count; 2025 DHCPv6ClientRef client = (DHCPv6ClientRef)arg; 2026 int i; 2027 interface_t * if_p = DHCPv6ClientGetInterface(client); 2028 2029 2030 if (changes == NULL || (count = CFArrayGetCount(changes)) == 0) { 2031 return; 2032 } 2033 2034 for (i = 0; i < count; i++) { 2035 CFStringRef key = CFArrayGetValueAtIndex(changes, i); 2036 2037 if (CFStringHasSuffix(key, kSCEntNetLink)) { 2038 CFBooleanRef active = kCFBooleanTrue; 2039 CFDictionaryRef dict; 2040 2041 my_log(LOG_NOTICE, "link changed"); 2042 dict = SCDynamicStoreCopyValue(session, key); 2043 if (dict != NULL) { 2044 if (CFDictionaryGetValue(dict, kSCPropNetLinkDetaching)) { 2045 my_log(LOG_NOTICE, "%s detaching - exiting", 2046 if_name(if_p)); 2047 exit(0); 2048 } 2049 active = CFDictionaryGetValue(dict, kSCPropNetLinkActive); 2050 } 2051 if (CFEqual(active, kCFBooleanTrue)) { 2052 DHCPv6ClientStart(client, S_allocate_address); 2053 } 2054 else { 2055 DHCPv6ClientStop(client, FALSE); 2056 } 2057 } 2058 else if (CFStringHasSuffix(key, kSCEntNetIPv6)) { 2059 inet6_addrlist_t addr_list; 2060 2061 my_log(LOG_NOTICE, "address changed"); 2062 /* get the addresses from the interface and deliver the event */ 2063 inet6_addrlist_copy(&addr_list, if_link_index(if_p)); 2064 DHCPv6ClientAddressChanged(client, &addr_list); 2065 inet6_addrlist_free(&addr_list); 2066 } 2067 } 2068 return; 2069} 2070 2071STATIC void 2072notification_init(DHCPv6ClientRef client) 2073{ 2074 CFArrayRef array; 2075 SCDynamicStoreContext context; 2076 CFStringRef ifname_cf; 2077 const void * keys[2]; 2078 CFRunLoopSourceRef rls; 2079 SCDynamicStoreRef store; 2080 2081 bzero(&context, sizeof(context)); 2082 context.info = client; 2083 store = SCDynamicStoreCreate(NULL, CFSTR("DHCPv6Client"), 2084 handle_change, &context); 2085 if (store == NULL) { 2086 my_log(LOG_ERR, "SCDynamicStoreCreate failed: %s", 2087 SCErrorString(SCError())); 2088 return; 2089 } 2090 ifname_cf 2091 = CFStringCreateWithCString(NULL, 2092 if_name(DHCPv6ClientGetInterface(client)), 2093 kCFStringEncodingASCII); 2094 keys[0] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 2095 kSCDynamicStoreDomainState, 2096 ifname_cf, 2097 kSCEntNetIPv6); 2098 keys[1] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 2099 kSCDynamicStoreDomainState, 2100 ifname_cf, 2101 kSCEntNetLink); 2102 CFRelease(ifname_cf); 2103 array = CFArrayCreate(NULL, (const void **)keys, 2, &kCFTypeArrayCallBacks); 2104 SCDynamicStoreSetNotificationKeys(store, array, NULL); 2105 CFRelease(array); 2106 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 2107 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 2108 CFRelease(rls); 2109 return; 2110} 2111 2112int 2113main(int argc, char * argv[]) 2114{ 2115 DHCPv6ClientRef client; 2116 interface_t * if_p; 2117 const char * ifname; 2118 interface_list_t * interfaces = NULL; 2119 2120 if (argc < 2) { 2121 fprintf(stderr, "%s <ifname>\n", argv[0]); 2122 exit(1); 2123 } 2124 interfaces = get_interface_list(); 2125 if (interfaces == NULL) { 2126 fprintf(stderr, "failed to get interface list\n"); 2127 exit(2); 2128 } 2129 ifname = argv[1]; 2130 if_p = ifl_find_name(interfaces, ifname); 2131 if (if_p == NULL) { 2132 fprintf(stderr, "No such interface '%s'\n", ifname); 2133 exit(2); 2134 } 2135 (void) openlog("DHCPv6Client", LOG_PERROR | LOG_PID, LOG_DAEMON); 2136 DHCPv6SocketVerbose(TRUE); 2137 client = DHCPv6ClientCreate(if_p); 2138 if (client == NULL) { 2139 fprintf(stderr, "DHCPv6ClientCreate(%s) failed\n", ifname); 2140 exit(2); 2141 } 2142 notification_init(client); 2143 DHCPv6ClientSetNotificationCallBack(client, client_notification, NULL); 2144 S_allocate_address = (argc > 2); 2145 DHCPv6ClientStart(client, S_allocate_address); 2146 CFRunLoopRun(); 2147 fprintf(stderr, "all done\n"); 2148 exit(0); 2149 return (0); 2150} 2151#endif /* TEST_DHCPV6_CLIENT */ 2152