Embed:
(wiki syntax)
Show/hide line numbers
dhcpv6_client.c
Go to the documentation of this file.
00001 /** 00002 * @file dhcpv6_client.c 00003 * @brief DHCPv6 client (Dynamic Host Configuration Protocol for IPv6) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @section Description 00026 * 00027 * The Dynamic Host Configuration Protocol for IPv6 enables DHCP servers to 00028 * pass configuration parameters such as IPv6 network addresses to IPv6 00029 * nodes. This protocol is a stateful counterpart to IPv6 Stateless Address 00030 * Autoconfiguration (RFC 2462), and can be used separately or concurrently 00031 * with the latter to obtain configuration parameters. Refer to RFC 3315 00032 * 00033 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00034 * @version 1.7.6 00035 **/ 00036 00037 //Switch to the appropriate trace level 00038 #define TRACE_LEVEL DHCPV6_TRACE_LEVEL 00039 00040 //Dependencies 00041 #include <stdlib.h> 00042 #include "core/net.h" 00043 #include "ipv6/ipv6.h" 00044 #include "ipv6/ipv6_misc.h" 00045 #include "ipv6/ndp.h" 00046 #include "dhcpv6/dhcpv6_client.h" 00047 #include "dhcpv6/dhcpv6_common.h" 00048 #include "dhcpv6/dhcpv6_debug.h" 00049 #include "dns/dns_common.h" 00050 #include "date_time.h" 00051 #include "debug.h" 00052 00053 //Check TCP/IP stack configuration 00054 #if (IPV6_SUPPORT == ENABLED && DHCPV6_CLIENT_SUPPORT == ENABLED) 00055 00056 //Tick counter to handle periodic operations 00057 systime_t dhcpv6ClientTickCounter; 00058 00059 //Requested DHCPv6 options 00060 static const uint16_t dhcpv6OptionList[] = 00061 { 00062 HTONS(DHCPV6_OPTION_DNS_SERVERS), 00063 HTONS(DHCPV6_OPTION_DOMAIN_LIST), 00064 HTONS(DHCPV6_OPTION_FQDN) 00065 }; 00066 00067 00068 /** 00069 * @brief Initialize settings with default values 00070 * @param[out] settings Structure that contains DHCPv6 client settings 00071 **/ 00072 00073 void dhcpv6ClientGetDefaultSettings(Dhcpv6ClientSettings *settings) 00074 { 00075 //Use default interface 00076 settings->interface = netGetDefaultInterface(); 00077 00078 //Support for quick configuration using rapid commit 00079 settings->rapidCommit = FALSE; 00080 //Use the DNS servers provided by the DHCPv6 server 00081 settings->manualDnsConfig = FALSE; 00082 //DHCPv6 configuration timeout 00083 settings->timeout = 0; 00084 //DHCPv6 configuration timeout event 00085 settings->timeoutEvent = NULL; 00086 //Link state change event 00087 settings->linkChangeEvent = NULL; 00088 //FSM state change event 00089 settings->stateChangeEvent = NULL; 00090 } 00091 00092 00093 /** 00094 * @brief DHCPv6 client initialization 00095 * @param[in] context Pointer to the DHCPv6 client context 00096 * @param[in] settings DHCPv6 client specific settings 00097 * @return Error code 00098 **/ 00099 00100 error_t dhcpv6ClientInit(Dhcpv6ClientContext *context, const Dhcpv6ClientSettings *settings) 00101 { 00102 error_t error; 00103 NetInterface *interface; 00104 00105 //Debug message 00106 TRACE_INFO("Initializing DHCPv6 client...\r\n"); 00107 00108 //Ensure the parameters are valid 00109 if(context == NULL || settings == NULL) 00110 return ERROR_INVALID_PARAMETER; 00111 00112 //A valid pointer to the interface being configured is required 00113 if(settings->interface == NULL) 00114 return ERROR_INVALID_PARAMETER; 00115 00116 //Point to the underlying network interface 00117 interface = settings->interface; 00118 00119 //Clear the DHCPv6 client context 00120 memset(context, 0, sizeof(Dhcpv6ClientContext)); 00121 //Save user settings 00122 context->settings = *settings; 00123 00124 //Generate client's DUID 00125 error = dhcpv6ClientGenerateDuid(context); 00126 //any error to report? 00127 if(error) 00128 return error; 00129 00130 //Generate client's fully qualified domain name 00131 error = dhcpv6ClientGenerateFqdn(context); 00132 //any error to report? 00133 if(error) 00134 return error; 00135 00136 //Callback function to be called when a DHCPv6 message is received 00137 error = udpAttachRxCallback(interface, DHCPV6_CLIENT_PORT, 00138 dhcpv6ClientProcessMessage, context); 00139 //Failed to register callback function? 00140 if(error) 00141 return error; 00142 00143 //DHCPv6 client is currently suspended 00144 context->running = FALSE; 00145 //Initialize state machine 00146 context->state = DHCPV6_STATE_INIT; 00147 00148 //Attach the DHCPv6 client context to the network interface 00149 interface->dhcpv6ClientContext = context; 00150 00151 //Successful initialization 00152 return NO_ERROR; 00153 } 00154 00155 00156 /** 00157 * @brief Start DHCPv6 client 00158 * @param[in] context Pointer to the DHCPv6 client context 00159 * @return Error code 00160 **/ 00161 00162 error_t dhcpv6ClientStart(Dhcpv6ClientContext *context) 00163 { 00164 NetInterface *interface; 00165 00166 //Check parameter 00167 if(context == NULL) 00168 return ERROR_INVALID_PARAMETER; 00169 00170 //Debug message 00171 TRACE_INFO("Starting DHCPv6 client...\r\n"); 00172 00173 //Get exclusive access 00174 osAcquireMutex(&netMutex); 00175 00176 //Point to the underlying network interface 00177 interface = context->settings.interface; 00178 00179 //Flush the list of IPv6 addresses from the client's IA 00180 dhcpv6ClientFlushAddrList(context); 00181 00182 //Automatic DNS server configuration? 00183 if(!context->settings.manualDnsConfig) 00184 { 00185 //Clear the list of DNS servers 00186 ipv6FlushDnsServerList(interface); 00187 } 00188 00189 //Check if the link is up? 00190 if(interface->linkState) 00191 { 00192 //A link-local address is formed by combining the well-known 00193 //link-local prefix fe80::/10 with the interface identifier 00194 dhcpv6ClientGenerateLinkLocalAddr(context); 00195 } 00196 00197 //Start DHCPv6 client 00198 context->running = TRUE; 00199 //Initialize state machine 00200 context->state = DHCPV6_STATE_INIT; 00201 00202 //Release exclusive access 00203 osReleaseMutex(&netMutex); 00204 00205 //Successful processing 00206 return NO_ERROR; 00207 } 00208 00209 00210 /** 00211 * @brief Stop DHCPv6 client 00212 * @param[in] context Pointer to the DHCPv6 client context 00213 * @return Error code 00214 **/ 00215 00216 error_t dhcpv6ClientStop(Dhcpv6ClientContext *context) 00217 { 00218 //Check parameter 00219 if(context == NULL) 00220 return ERROR_INVALID_PARAMETER; 00221 00222 //Debug message 00223 TRACE_INFO("Stopping DHCPv6 client...\r\n"); 00224 00225 //Get exclusive access 00226 osAcquireMutex(&netMutex); 00227 00228 //Stop DHCPv6 client 00229 context->running = FALSE; 00230 //Reinitialize state machine 00231 context->state = DHCPV6_STATE_INIT; 00232 00233 //Release exclusive access 00234 osReleaseMutex(&netMutex); 00235 00236 //Successful processing 00237 return NO_ERROR; 00238 } 00239 00240 00241 /** 00242 * @brief Release DHCPv6 lease 00243 * @param[in] context Pointer to the DHCPv6 client context 00244 * @return Error code 00245 **/ 00246 00247 error_t dhcpv6ClientRelease(Dhcpv6ClientContext *context) 00248 { 00249 uint_t i; 00250 NetInterface *interface; 00251 Dhcpv6ClientAddrEntry *entry; 00252 00253 //Check parameter 00254 if(context == NULL) 00255 return ERROR_INVALID_PARAMETER; 00256 00257 //Debug message 00258 TRACE_INFO("Releasing DHCPv6 lease...\r\n"); 00259 00260 //Get exclusive access 00261 osAcquireMutex(&netMutex); 00262 00263 //Point to the underlying network interface 00264 interface = context->settings.interface; 00265 00266 //Check whether the DHCPv6 client is running 00267 if(context->running) 00268 { 00269 //BOUND state? 00270 if(context->state == DHCPV6_STATE_BOUND) 00271 { 00272 //Loop through the IPv6 addresses recorded by the DHCPv6 client 00273 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 00274 { 00275 //Point to the current entry 00276 entry = &context->ia.addrList[i]; 00277 00278 //Valid IPv6 address? 00279 if(entry->validLifetime > 0) 00280 { 00281 //The client must stop using the addresses being released as soon 00282 //as the client begins the Release message exchange process 00283 ipv6RemoveAddr(interface, &entry->addr); 00284 } 00285 } 00286 00287 //Switch to the RELEASE state 00288 dhcpv6ClientChangeState(context, DHCPV6_STATE_RELEASE, 0); 00289 } 00290 else 00291 { 00292 //Stop DHCPv6 client 00293 context->running = FALSE; 00294 //Reinitialize state machine 00295 context->state = DHCPV6_STATE_INIT; 00296 } 00297 } 00298 00299 //Release exclusive access 00300 osReleaseMutex(&netMutex); 00301 00302 //Successful processing 00303 return NO_ERROR; 00304 } 00305 00306 00307 /** 00308 * @brief Retrieve current state 00309 * @param[in] context Pointer to the DHCPv6 client context 00310 * @return Current DHCPv6 client state 00311 **/ 00312 00313 Dhcpv6State dhcpv6ClientGetState(Dhcpv6ClientContext *context) 00314 { 00315 Dhcpv6State state; 00316 00317 //Get exclusive access 00318 osAcquireMutex(&netMutex); 00319 //Get current state 00320 state = context->state; 00321 //Release exclusive access 00322 osReleaseMutex(&netMutex); 00323 00324 //Return current state 00325 return state; 00326 } 00327 00328 00329 /** 00330 * @brief DHCPv6 client timer handler 00331 * 00332 * This routine must be periodically called by the TCP/IP stack to 00333 * manage DHCPv6 client operation 00334 * 00335 * @param[in] context Pointer to the DHCPv6 client context 00336 **/ 00337 00338 00339 void dhcpv6ClientTick(Dhcpv6ClientContext *context) 00340 { 00341 //Make sure the DHCPv6 client has been properly instantiated 00342 if(context == NULL) 00343 return; 00344 00345 //DHCPv6 client finite state machine 00346 switch(context->state) 00347 { 00348 //Process INIT state 00349 case DHCPV6_STATE_INIT: 00350 //This is the initialization state, where a client begins the process of 00351 //acquiring a lease. It also returns here when a lease ends, or when a 00352 //lease negotiation fails 00353 dhcpv6ClientStateInit(context); 00354 break; 00355 //Process SOLICIT state 00356 case DHCPV6_STATE_SOLICIT: 00357 //The client sends a Solicit message to locate servers 00358 dhcpv6ClientStateSolicit(context); 00359 break; 00360 //Process REQUEST state 00361 case DHCPV6_STATE_REQUEST: 00362 //The client sends a Request message to request configuration 00363 //parameters, including IP addresses, from a specific server 00364 dhcpv6ClientStateRequest(context); 00365 break; 00366 //Process INIT-CONFIRM state 00367 case DHCPV6_STATE_INIT_CONFIRM: 00368 //When a client that already has a valid lease starts up after a 00369 //power-down or reboot, it starts here instead of the INIT state 00370 dhcpv6ClientStateInitConfirm(context); 00371 break; 00372 //Process CONFIRM state 00373 case DHCPV6_STATE_CONFIRM: 00374 //The client sends a Confirm message to any available server 00375 //to determine whether the addresses it was assigned are still 00376 //appropriate to the link to which the client is connected 00377 dhcpv6ClientStateConfirm(context); 00378 break; 00379 //Process DAD state 00380 case DHCPV6_STATE_DAD: 00381 //The client should perform duplicate address detection on each 00382 //of the addresses in any IAs it receives in the Reply message 00383 //before using that address for traffic 00384 dhcpv6ClientStateDad(context); 00385 break; 00386 //Process BOUND state 00387 case DHCPV6_STATE_BOUND: 00388 //The client has a valid lease and is in its normal operating state 00389 dhcpv6ClientStateBound(context); 00390 break; 00391 //Process RENEW state 00392 case DHCPV6_STATE_RENEW: 00393 //The client sends a Renew message to the server that originally 00394 //provided the client's addresses and configuration parameters to 00395 //extend the lifetimes on the addresses assigned to the client 00396 //and to update other configuration parameters 00397 dhcpv6ClientStateRenew(context); 00398 break; 00399 //Process REBIND state 00400 case DHCPV6_STATE_REBIND: 00401 //The client sends a Rebind message to any available server to extend 00402 //the lifetimes on the addresses assigned to the client and to update 00403 //other configuration parameters. This message is sent after a client 00404 //receives no response to a Renew message 00405 dhcpv6ClientStateRebind(context); 00406 break; 00407 //Process RELEASE state 00408 case DHCPV6_STATE_RELEASE: 00409 //To release one or more addresses, a client sends a Release message 00410 //to the server 00411 dhcpv6ClientStateRelease(context); 00412 break; 00413 //Process DECLINE state 00414 case DHCPV6_STATE_DECLINE: 00415 //If a client detects that one or more addresses assigned to it by a 00416 //server are already in use by another node, the client sends a Decline 00417 //message to the server to inform it that the address is suspect 00418 dhcpv6ClientStateDecline(context); 00419 break; 00420 //Invalid state... 00421 default: 00422 //Switch to the default state 00423 context->state = DHCPV6_STATE_INIT; 00424 break; 00425 } 00426 } 00427 00428 00429 /** 00430 * @brief Callback function for link change event 00431 * @param[in] context Pointer to the DHCPv6 client context 00432 **/ 00433 00434 void dhcpv6ClientLinkChangeEvent(Dhcpv6ClientContext *context) 00435 { 00436 NetInterface *interface; 00437 00438 //Make sure the DHCPv6 client has been properly instantiated 00439 if(context == NULL) 00440 return; 00441 00442 //Point to the underlying network interface 00443 interface = context->settings.interface; 00444 00445 //Check whether the DHCPv6 client is running 00446 if(context->running) 00447 { 00448 //Automatic DNS server configuration? 00449 if(!context->settings.manualDnsConfig) 00450 { 00451 //Clear the list of DNS servers 00452 ipv6FlushDnsServerList(interface); 00453 } 00454 00455 //Link-up event? 00456 if(interface->linkState) 00457 { 00458 //A link-local address is formed by combining the well-known 00459 //link-local prefix fe80::/10 with the interface identifier 00460 dhcpv6ClientGenerateLinkLocalAddr(context); 00461 } 00462 } 00463 00464 //Check the state of the DHCPv6 client 00465 switch(context->state) 00466 { 00467 case DHCPV6_STATE_INIT_CONFIRM: 00468 case DHCPV6_STATE_CONFIRM : 00469 case DHCPV6_STATE_DAD: 00470 case DHCPV6_STATE_BOUND: 00471 case DHCPV6_STATE_RENEW: 00472 case DHCPV6_STATE_REBIND: 00473 //The client already has a valid lease 00474 context->state = DHCPV6_STATE_INIT_CONFIRM; 00475 break; 00476 case DHCPV6_STATE_RELEASE: 00477 //Stop DHCPv6 client 00478 context->running = FALSE; 00479 //Reinitialize state machine 00480 context->state = DHCPV6_STATE_INIT; 00481 break; 00482 default: 00483 //Switch to the INIT state 00484 context->state = DHCPV6_STATE_INIT; 00485 break; 00486 } 00487 00488 //Any registered callback? 00489 if(context->settings.linkChangeEvent != NULL) 00490 { 00491 //Release exclusive access 00492 osReleaseMutex(&netMutex); 00493 //Invoke user callback function 00494 context->settings.linkChangeEvent(context, interface, interface->linkState); 00495 //Get exclusive access 00496 osAcquireMutex(&netMutex); 00497 } 00498 } 00499 00500 00501 /** 00502 * @brief INIT state 00503 * 00504 * This is the initialization state, where a client begins the process of 00505 * acquiring a lease. It also returns here when a lease ends, or when a 00506 * lease negotiation fails 00507 * 00508 * @param[in] context Pointer to the DHCPv6 client context 00509 **/ 00510 00511 void dhcpv6ClientStateInit(Dhcpv6ClientContext *context) 00512 { 00513 systime_t delay; 00514 NetInterface *interface; 00515 00516 //Point to the underlying network interface 00517 interface = context->settings.interface; 00518 00519 //Check whether the DHCPv6 client is running 00520 if(context->running) 00521 { 00522 //Wait for the link to be up before starting DHCPv6 configuration 00523 if(interface->linkState) 00524 { 00525 //Make sure that a valid link-local address has been assigned to the interface 00526 if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) 00527 { 00528 //Flush the list of IPv6 addresses from the client's IA 00529 dhcpv6ClientFlushAddrList(context); 00530 00531 //The first Solicit message from the client on the interface must be 00532 //delayed by a random amount of time between 0 and SOL_MAX_DELAY 00533 delay = dhcpv6RandRange(0, DHCPV6_CLIENT_SOL_MAX_DELAY); 00534 00535 //Record the time at which the client started 00536 //the address acquisition process 00537 context->configStartTime = osGetSystemTime(); 00538 //Clear flag 00539 context->timeoutEventDone = FALSE; 00540 00541 //Switch to the SOLICIT state 00542 dhcpv6ClientChangeState(context, DHCPV6_STATE_SOLICIT, delay); 00543 } 00544 } 00545 } 00546 } 00547 00548 00549 /** 00550 * @brief SOLICIT state 00551 * 00552 * A client uses the Solicit message to discover DHCPv6 servers 00553 * 00554 * @param[in] context Pointer to the DHCPv6 client context 00555 **/ 00556 00557 void dhcpv6ClientStateSolicit(Dhcpv6ClientContext *context) 00558 { 00559 systime_t time; 00560 00561 //Get current time 00562 time = osGetSystemTime(); 00563 00564 //Check current time 00565 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00566 { 00567 //Check retransmission counter 00568 if(context->retransmitCount == 0) 00569 { 00570 //Reset server preference value 00571 context->serverPreference = -1; 00572 //Generate a 24-bit transaction ID 00573 context->transactionId = netGetRand() & 0x00FFFFFF; 00574 00575 //Send a Solicit message 00576 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT); 00577 00578 //Save the time at which the message was sent 00579 context->exchangeStartTime = time; 00580 context->timestamp = time; 00581 00582 //If the client is waiting for an Advertise message, the first RT 00583 //must be selected to be strictly greater than IRT 00584 context->timeout = DHCPV6_CLIENT_SOL_TIMEOUT + 00585 abs(dhcpv6Rand(DHCPV6_CLIENT_SOL_TIMEOUT)); 00586 00587 //Increment retransmission counter 00588 context->retransmitCount++; 00589 } 00590 else 00591 { 00592 //Check whether a valid Advertise message has been received 00593 if(context->serverPreference >= 0) 00594 { 00595 //Continue configuration procedure 00596 dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); 00597 } 00598 else 00599 { 00600 //Send a Solicit message 00601 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_SOLICIT); 00602 00603 //Save the time at which the message was sent 00604 context->timestamp = time; 00605 00606 //The RT is doubled for each subsequent retransmission 00607 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 00608 00609 //MRT specifies an upper bound on the value of RT 00610 if(context->timeout > DHCPV6_CLIENT_SOL_MAX_RT) 00611 { 00612 //Compute retransmission timeout 00613 context->timeout = DHCPV6_CLIENT_SOL_MAX_RT + 00614 dhcpv6Rand(DHCPV6_CLIENT_SOL_MAX_RT); 00615 } 00616 00617 //Increment retransmission counter 00618 context->retransmitCount++; 00619 } 00620 } 00621 } 00622 00623 //Manage DHCPv6 configuration timeout 00624 dhcpv6ClientCheckTimeout(context); 00625 } 00626 00627 00628 /** 00629 * @brief REQUEST state 00630 * 00631 * The client uses a Request message to populate IAs with addresses and obtain 00632 * other configuration information. The client includes one or more more IA 00633 * options in the Request message. The server then returns addresses and other 00634 * information about the IAs to the client in IA options in a Reply message 00635 * 00636 * @param[in] context Pointer to the DHCPv6 client context 00637 **/ 00638 00639 void dhcpv6ClientStateRequest(Dhcpv6ClientContext *context) 00640 { 00641 systime_t time; 00642 00643 //Get current time 00644 time = osGetSystemTime(); 00645 00646 //Check current time 00647 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00648 { 00649 //Check retransmission counter 00650 if(context->retransmitCount == 0) 00651 { 00652 //Generate a 24-bit transaction ID 00653 context->transactionId = netGetRand() & 0x00FFFFFF; 00654 00655 //Send a Request message 00656 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST); 00657 00658 //Save the time at which the message was sent 00659 context->exchangeStartTime = time; 00660 context->timestamp = time; 00661 00662 //Initial retransmission timeout 00663 context->timeout = DHCPV6_CLIENT_REQ_TIMEOUT + 00664 dhcpv6Rand(DHCPV6_CLIENT_REQ_TIMEOUT); 00665 00666 //Increment retransmission counter 00667 context->retransmitCount++; 00668 } 00669 else if(context->retransmitCount < DHCPV6_CLIENT_REQ_MAX_RC) 00670 { 00671 //Send a Request message 00672 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REQUEST); 00673 00674 //Save the time at which the message was sent 00675 context->timestamp = time; 00676 00677 //The RT is doubled for each subsequent retransmission 00678 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 00679 00680 //MRT specifies an upper bound on the value of RT 00681 if(context->timeout > DHCPV6_CLIENT_REQ_MAX_RT) 00682 { 00683 //Compute retransmission timeout 00684 context->timeout = DHCPV6_CLIENT_REQ_MAX_RT + 00685 dhcpv6Rand(DHCPV6_CLIENT_REQ_MAX_RT); 00686 } 00687 00688 //Increment retransmission counter 00689 context->retransmitCount++; 00690 } 00691 else 00692 { 00693 //If the client does not receive a response within a reasonable 00694 //period of time, then it restarts the initialization procedure 00695 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 00696 } 00697 } 00698 00699 //Manage DHCPv6 configuration timeout 00700 dhcpv6ClientCheckTimeout(context); 00701 } 00702 00703 00704 /** 00705 * @brief INIT-CONFIRM state 00706 * 00707 * When a client that already has a valid lease starts up after a 00708 * power-down or reboot, it starts here instead of the INIT state 00709 * 00710 * @param[in] context Pointer to the DHCPv6 client context 00711 **/ 00712 00713 void dhcpv6ClientStateInitConfirm(Dhcpv6ClientContext *context) 00714 { 00715 systime_t delay; 00716 NetInterface *interface; 00717 00718 //Point to the underlying network interface 00719 interface = context->settings.interface; 00720 00721 //Check whether the DHCPv6 client is running 00722 if(context->running) 00723 { 00724 //Wait for the link to be up before starting DHCPv6 configuration 00725 if(interface->linkState) 00726 { 00727 //Make sure that a valid link-local address has been assigned to the interface 00728 if(ipv6GetLinkLocalAddrState(interface) == IPV6_ADDR_STATE_PREFERRED) 00729 { 00730 //The first Confirm message from the client on the interface must be 00731 //delayed by a random amount of time between 0 and CNF_MAX_DELAY 00732 delay = dhcpv6RandRange(0, DHCPV6_CLIENT_CNF_MAX_DELAY); 00733 00734 //Record the time at which the client started 00735 //the address acquisition process 00736 context->configStartTime = osGetSystemTime(); 00737 //Clear flag 00738 context->timeoutEventDone = FALSE; 00739 00740 //Switch to the CONFIRM state 00741 dhcpv6ClientChangeState(context, DHCPV6_STATE_CONFIRM, delay); 00742 } 00743 } 00744 } 00745 } 00746 00747 00748 /** 00749 * @brief CONFIRM state 00750 * 00751 * Whenever a client may have moved to a new link, the prefixes from 00752 * the addresses assigned to the interfaces on that link may no longer 00753 * be appropriate for the link to which the client is attached. In such 00754 * the client must initiate a Confirm/Reply message exchange 00755 * 00756 * @param[in] context Pointer to the DHCPv6 client context 00757 **/ 00758 00759 void dhcpv6ClientStateConfirm(Dhcpv6ClientContext *context) 00760 { 00761 systime_t time; 00762 00763 //Get current time 00764 time = osGetSystemTime(); 00765 00766 //Check current time 00767 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00768 { 00769 //Check retransmission counter 00770 if(context->retransmitCount == 0) 00771 { 00772 //Generate a 24-bit transaction ID 00773 context->transactionId = netGetRand() & 0x00FFFFFF; 00774 00775 //Send a Confirm message 00776 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM); 00777 00778 //Save the time at which the client sent the first message 00779 context->exchangeStartTime = time; 00780 context->timestamp = time; 00781 00782 //Initial retransmission timeout 00783 context->timeout = DHCPV6_CLIENT_CNF_TIMEOUT + 00784 dhcpv6Rand(DHCPV6_CLIENT_CNF_TIMEOUT); 00785 00786 //Increment retransmission counter 00787 context->retransmitCount++; 00788 } 00789 else 00790 { 00791 //Send a Confirm message 00792 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_CONFIRM); 00793 00794 //Save the time at which the message was sent 00795 context->timestamp = time; 00796 00797 //The RT is doubled for each subsequent retransmission 00798 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 00799 00800 //MRT specifies an upper bound on the value of RT 00801 if(context->timeout > DHCPV6_CLIENT_CNF_MAX_RT) 00802 { 00803 //Compute retransmission timeout 00804 context->timeout = DHCPV6_CLIENT_CNF_MAX_RT + 00805 dhcpv6Rand(DHCPV6_CLIENT_CNF_MAX_RT); 00806 } 00807 00808 //Increment retransmission counter 00809 context->retransmitCount++; 00810 } 00811 } 00812 else 00813 { 00814 //Check retransmission counter 00815 if(context->retransmitCount > 0) 00816 { 00817 //The message exchange fails once MRD seconds have elapsed since 00818 //the client first transmitted the message 00819 if(timeCompare(time, context->exchangeStartTime + DHCPV6_CLIENT_CNF_MAX_RD) >= 0) 00820 { 00821 //If the client receives no responses before the message transmission 00822 //process terminates, the client should continue to use any IP 00823 //addresses using the last known lifetimes for those addresses 00824 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 00825 } 00826 } 00827 } 00828 00829 //Manage DHCPv6 configuration timeout 00830 dhcpv6ClientCheckTimeout(context); 00831 } 00832 00833 00834 /** 00835 * @brief DAD state 00836 * 00837 * The client perform duplicate address detection on each 00838 * of the addresses in any IAs it receives in the Reply message 00839 * before using that address for traffic 00840 * 00841 * @param[in] context Pointer to the DHCPv6 client context 00842 **/ 00843 00844 void dhcpv6ClientStateDad(Dhcpv6ClientContext *context) 00845 { 00846 uint_t i; 00847 NetInterface *interface; 00848 Ipv6AddrState state; 00849 Dhcpv6ClientAddrEntry *entry; 00850 00851 //Point to the underlying network interface 00852 interface = context->settings.interface; 00853 00854 //Loop through the IPv6 addresses recorded by the DHCPv6 client 00855 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 00856 { 00857 //Point to the current entry 00858 entry = &context->ia.addrList[i]; 00859 00860 //Check the IPv6 address is a tentative address? 00861 if(entry->validLifetime > 0) 00862 { 00863 //Get the state of the current IPv6 address 00864 state = ipv6GetAddrState(interface, &entry->addr); 00865 00866 //Duplicate Address Detection in progress? 00867 if(state == IPV6_ADDR_STATE_TENTATIVE) 00868 { 00869 //Exit immediately 00870 return; 00871 } 00872 //Duplicate Address Detection failed? 00873 else if(state == IPV6_ADDR_STATE_INVALID) 00874 { 00875 //Switch to the DECLINE state 00876 dhcpv6ClientChangeState(context, DHCPV6_STATE_DECLINE, 0); 00877 //Exit immediately 00878 return; 00879 } 00880 } 00881 } 00882 00883 //Dump current DHCPv6 configuration for debugging purpose 00884 dhcpv6ClientDumpConfig(context); 00885 //Switch to the BOUND state 00886 dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0); 00887 } 00888 00889 00890 /** 00891 * @brief BOUND state 00892 * 00893 * Client has a valid lease and is in its normal operating state 00894 * 00895 * @param[in] context Pointer to the DHCPv6 client context 00896 **/ 00897 00898 void dhcpv6ClientStateBound(Dhcpv6ClientContext *context) 00899 { 00900 systime_t t1; 00901 systime_t time; 00902 00903 //Get current time 00904 time = osGetSystemTime(); 00905 00906 //A client will never attempt to extend the lifetime of any 00907 //address in an IA with T1 set to 0xffffffff 00908 if(context->ia.t1 != DHCPV6_INFINITE_TIME) 00909 { 00910 //Convert T1 to milliseconds 00911 if(context->ia.t1 < (MAX_DELAY / 1000)) 00912 t1 = context->ia.t1 * 1000; 00913 else 00914 t1 = MAX_DELAY; 00915 00916 //Check the time elapsed since the lease was obtained 00917 if(timeCompare(time, context->leaseStartTime + t1) >= 0) 00918 { 00919 //Record the time at which the client started the address renewal process 00920 context->configStartTime = time; 00921 00922 //Enter the RENEW state 00923 dhcpv6ClientChangeState(context, DHCPV6_STATE_RENEW, 0); 00924 } 00925 } 00926 } 00927 00928 00929 /** 00930 * @brief RENEW state 00931 * 00932 * The client sends a Renew message to the server that originally 00933 * provided the client's addresses and configuration parameters to 00934 * extend the lifetimes on the addresses assigned to the client 00935 * and to update other configuration parameters 00936 * 00937 * @param[in] context Pointer to the DHCPv6 client context 00938 **/ 00939 00940 void dhcpv6ClientStateRenew(Dhcpv6ClientContext *context) 00941 { 00942 systime_t t2; 00943 systime_t time; 00944 00945 //Get current time 00946 time = osGetSystemTime(); 00947 00948 //Check current time 00949 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 00950 { 00951 //Check retransmission counter 00952 if(context->retransmitCount == 0) 00953 { 00954 //Generate a 24-bit transaction ID 00955 context->transactionId = netGetRand() & 0x00FFFFFF; 00956 00957 //Send a Renew message 00958 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW); 00959 00960 //Save the time at which the message was sent 00961 context->exchangeStartTime = time; 00962 context->timestamp = time; 00963 00964 //Initial retransmission timeout 00965 context->timeout = DHCPV6_CLIENT_REN_TIMEOUT + 00966 dhcpv6Rand(DHCPV6_CLIENT_REN_TIMEOUT); 00967 } 00968 else 00969 { 00970 //Send a Renew message 00971 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RENEW); 00972 00973 //Save the time at which the message was sent 00974 context->timestamp = time; 00975 00976 //The RT is doubled for each subsequent retransmission 00977 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 00978 00979 //MRT specifies an upper bound on the value of RT 00980 if(context->timeout > DHCPV6_CLIENT_REN_MAX_RT) 00981 { 00982 //Compute retransmission timeout 00983 context->timeout = DHCPV6_CLIENT_REN_MAX_RT + 00984 dhcpv6Rand(DHCPV6_CLIENT_REN_MAX_RT); 00985 } 00986 } 00987 00988 //Increment retransmission counter 00989 context->retransmitCount++; 00990 } 00991 else 00992 { 00993 //A client will never attempt to use a Rebind message to locate a 00994 //different server to extend the lifetime of any address in an IA 00995 //with T2 set to 0xffffffff 00996 if(context->ia.t2 != DHCPV6_INFINITE_TIME) 00997 { 00998 //Convert T2 to milliseconds 00999 if(context->ia.t2 < (MAX_DELAY / 1000)) 01000 t2 = context->ia.t2 * 1000; 01001 else 01002 t2 = MAX_DELAY; 01003 01004 //Check whether T2 timer has expired 01005 if(timeCompare(time, context->leaseStartTime + t2) >= 0) 01006 { 01007 //Switch to the REBIND state 01008 dhcpv6ClientChangeState(context, DHCPV6_STATE_REBIND, 0); 01009 } 01010 } 01011 } 01012 } 01013 01014 01015 /** 01016 * @brief REBIND state 01017 * 01018 * The client sends a Rebind message to any available server to extend 01019 * the lifetimes on the addresses assigned to the client and to update 01020 * other configuration parameters. This message is sent after a client 01021 * receives no response to a Renew message 01022 * 01023 * @param[in] context Pointer to the DHCPv6 client context 01024 **/ 01025 01026 void dhcpv6ClientStateRebind(Dhcpv6ClientContext *context) 01027 { 01028 uint_t i; 01029 systime_t time; 01030 NetInterface *interface; 01031 Dhcpv6ClientAddrEntry *entry; 01032 01033 //Point to the underlying network interface 01034 interface = context->settings.interface; 01035 01036 //Get current time 01037 time = osGetSystemTime(); 01038 01039 //Check current time 01040 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 01041 { 01042 //Check retransmission counter 01043 if(context->retransmitCount == 0) 01044 { 01045 //Generate a 24-bit transaction ID 01046 context->transactionId = netGetRand() & 0x00FFFFFF; 01047 01048 //Send a Rebind message 01049 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND); 01050 01051 //Save the time at which the message was sent 01052 context->exchangeStartTime = time; 01053 context->timestamp = time; 01054 01055 //Initial retransmission timeout 01056 context->timeout = DHCPV6_CLIENT_REB_TIMEOUT + 01057 dhcpv6Rand(DHCPV6_CLIENT_REB_TIMEOUT); 01058 } 01059 else 01060 { 01061 //Send a Rebind message 01062 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_REBIND); 01063 01064 //Save the time at which the message was sent 01065 context->timestamp = time; 01066 01067 //The RT is doubled for each subsequent retransmission 01068 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 01069 01070 //MRT specifies an upper bound on the value of RT 01071 if(context->timeout > DHCPV6_CLIENT_REB_MAX_RT) 01072 { 01073 //Compute retransmission timeout 01074 context->timeout = DHCPV6_CLIENT_REB_MAX_RT + 01075 dhcpv6Rand(DHCPV6_CLIENT_REB_MAX_RT); 01076 } 01077 } 01078 01079 //Increment retransmission counter 01080 context->retransmitCount++; 01081 } 01082 else 01083 { 01084 //Loop through the IPv6 addresses recorded by the DHCPv6 client 01085 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 01086 { 01087 //Point to the current entry 01088 entry = &context->ia.addrList[i]; 01089 01090 //Valid IPv6 address? 01091 if(entry->validLifetime > 0) 01092 { 01093 //Check whether the valid lifetime has expired 01094 if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_INVALID) 01095 { 01096 //Restart DHCPv6 configuration 01097 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01098 } 01099 } 01100 } 01101 } 01102 } 01103 01104 01105 /** 01106 * @brief RELEASE state 01107 * 01108 * To release one or more addresses, a client sends a Release message 01109 * to the server 01110 * 01111 * @param[in] context Pointer to the DHCPv6 client context 01112 **/ 01113 01114 void dhcpv6ClientStateRelease(Dhcpv6ClientContext *context) 01115 { 01116 systime_t time; 01117 01118 //Get current time 01119 time = osGetSystemTime(); 01120 01121 //Check current time 01122 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 01123 { 01124 //Check retransmission counter 01125 if(context->retransmitCount == 0) 01126 { 01127 //Generate a 24-bit transaction ID 01128 context->transactionId = netGetRand() & 0x00FFFFFF; 01129 01130 //Send a Release message 01131 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE); 01132 01133 //Save the time at which the message was sent 01134 context->exchangeStartTime = time; 01135 context->timestamp = time; 01136 01137 //Initial retransmission timeout 01138 context->timeout = DHCPV6_CLIENT_REL_TIMEOUT + 01139 dhcpv6Rand(DHCPV6_CLIENT_REL_TIMEOUT); 01140 01141 //Increment retransmission counter 01142 context->retransmitCount++; 01143 } 01144 else if(context->retransmitCount < DHCPV6_CLIENT_REL_MAX_RC) 01145 { 01146 //Send a Release message 01147 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_RELEASE); 01148 01149 //Save the time at which the message was sent 01150 context->timestamp = time; 01151 01152 //The RT is doubled for each subsequent retransmission 01153 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 01154 01155 //Increment retransmission counter 01156 context->retransmitCount++; 01157 } 01158 else 01159 { 01160 //Implementations should retransmit one or more times, but may 01161 //choose to terminate the retransmission procedure early 01162 context->running = FALSE; 01163 01164 //Reinitialize state machine 01165 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01166 } 01167 } 01168 } 01169 01170 01171 /** 01172 * @brief DECLINE state 01173 * 01174 * If a client detects that one or more addresses assigned to it by a 01175 * server are already in use by another node, the client sends a Decline 01176 * message to the server to inform it that the address is suspect 01177 * 01178 * @param[in] context Pointer to the DHCPv6 client context 01179 **/ 01180 01181 void dhcpv6ClientStateDecline(Dhcpv6ClientContext *context) 01182 { 01183 systime_t time; 01184 01185 //Get current time 01186 time = osGetSystemTime(); 01187 01188 //Check current time 01189 if(timeCompare(time, context->timestamp + context->timeout) >= 0) 01190 { 01191 //Check retransmission counter 01192 if(context->retransmitCount == 0) 01193 { 01194 //Generate a 24-bit transaction ID 01195 context->transactionId = netGetRand() & 0x00FFFFFF; 01196 01197 //Send a Decline message 01198 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE); 01199 01200 //Save the time at which the message was sent 01201 context->exchangeStartTime = time; 01202 context->timestamp = time; 01203 01204 //Initial retransmission timeout 01205 context->timeout = DHCPV6_CLIENT_DEC_TIMEOUT + 01206 dhcpv6Rand(DHCPV6_CLIENT_DEC_TIMEOUT); 01207 01208 //Increment retransmission counter 01209 context->retransmitCount++; 01210 } 01211 else if(context->retransmitCount < DHCPV6_CLIENT_DEC_MAX_RC) 01212 { 01213 //Send a Decline message 01214 dhcpv6ClientSendMessage(context, DHCPV6_MSG_TYPE_DECLINE); 01215 01216 //Save the time at which the message was sent 01217 context->timestamp = time; 01218 01219 //The RT is doubled for each subsequent retransmission 01220 context->timeout = context->timeout * 2 + dhcpv6Rand(context->timeout); 01221 01222 //Increment retransmission counter 01223 context->retransmitCount++; 01224 } 01225 else 01226 { 01227 //If the client does not receive a response within a reasonable 01228 //period of time, then it restarts the initialization procedure 01229 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01230 } 01231 } 01232 } 01233 01234 01235 /** 01236 * @brief Send Solicit message 01237 * @param[in] context Pointer to the DHCPv6 client context 01238 * @param[in] type DHCPv6 message type 01239 * @return Error code 01240 **/ 01241 01242 error_t dhcpv6ClientSendMessage(Dhcpv6ClientContext *context, 01243 Dhcpv6MessageType type) 01244 { 01245 error_t error; 01246 uint_t i; 01247 size_t length; 01248 size_t offset; 01249 NetBuffer *buffer; 01250 NetInterface *interface; 01251 Dhcpv6Message *message; 01252 Dhcpv6Option *option; 01253 Dhcpv6IaNaOption iaNaOption; 01254 Dhcpv6IaAddrOption iaAddrOption; 01255 Dhcpv6FqdnOption *fqdnOption; 01256 Dhcpv6ElapsedTimeOption elapsedTimeOption; 01257 Dhcpv6ClientAddrEntry *entry; 01258 IpAddr destIpAddr; 01259 01260 //Point to the underlying network interface 01261 interface = context->settings.interface; 01262 01263 //Allocate a memory buffer to hold the DHCPv6 message 01264 buffer = udpAllocBuffer(DHCPV6_MAX_MSG_SIZE, &offset); 01265 //Failed to allocate buffer? 01266 if(buffer == NULL) 01267 return ERROR_OUT_OF_MEMORY; 01268 01269 //Point to the beginning of the DHCPv6 message 01270 message = netBufferAt(buffer, offset); 01271 01272 //Set DHCPv6 message type 01273 message->msgType = type; 01274 01275 //The transaction ID is chosen by the client 01276 STORE24BE(context->transactionId, message->transactionId); 01277 01278 //Size of the DHCPv6 message 01279 length = sizeof(Dhcpv6Message); 01280 01281 //The client must include a Client Identifier option 01282 //to identify itself to the server 01283 dhcpv6AddOption(message, &length, DHCPV6_OPTION_CLIENTID, 01284 context->clientId, context->clientIdLength); 01285 01286 //Request, Renew, Release or Decline message? 01287 if(type == DHCPV6_MSG_TYPE_REQUEST || 01288 type == DHCPV6_MSG_TYPE_RENEW || 01289 type == DHCPV6_MSG_TYPE_RELEASE || 01290 type == DHCPV6_MSG_TYPE_DECLINE) 01291 { 01292 //The client places the identifier of the destination 01293 //server in a Server Identifier option 01294 dhcpv6AddOption(message, &length, DHCPV6_OPTION_SERVERID, 01295 context->serverId, context->serverIdLength); 01296 } 01297 01298 //Solicit message? 01299 if(type == DHCPV6_MSG_TYPE_SOLICIT) 01300 { 01301 //Check whether rapid commit is enabled 01302 if(context->settings.rapidCommit) 01303 { 01304 //Include the Rapid Commit option if the client is prepared 01305 //to perform the Solicit/Reply message exchange 01306 dhcpv6AddOption(message, &length, DHCPV6_OPTION_RAPID_COMMIT, NULL, 0); 01307 } 01308 } 01309 01310 //Solicit, Request, Confirm, Renew or Rebind message? 01311 if(type == DHCPV6_MSG_TYPE_SOLICIT || 01312 type == DHCPV6_MSG_TYPE_REQUEST || 01313 type == DHCPV6_MSG_TYPE_CONFIRM || 01314 type == DHCPV6_MSG_TYPE_RENEW || 01315 type == DHCPV6_MSG_TYPE_REBIND) 01316 { 01317 //Point to the client's fully qualified domain name 01318 fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn; 01319 01320 //The FQDN option can be used by the client to convey its 01321 //fully qualified domain name to the server 01322 dhcpv6AddOption(message, &length, DHCPV6_OPTION_FQDN, 01323 fqdnOption, sizeof(Dhcpv6FqdnOption) + context->clientFqdnLength); 01324 01325 //The client should include an Option Request option to indicate 01326 //the options the client is interested in receiving 01327 dhcpv6AddOption(message, &length, DHCPV6_OPTION_ORO, 01328 &dhcpv6OptionList, sizeof(dhcpv6OptionList)); 01329 } 01330 01331 //Prepare an IA_NA option for a the current interface 01332 iaNaOption.iaId = htonl(interface->id); 01333 01334 //Solicit, Request or Confirm message? 01335 if(type == DHCPV6_MSG_TYPE_SOLICIT || 01336 type == DHCPV6_MSG_TYPE_REQUEST || 01337 type == DHCPV6_MSG_TYPE_CONFIRM) 01338 { 01339 //The client should set the T1 and T2 fields in any IA_NA options to 0 01340 iaNaOption.t1 = 0; 01341 iaNaOption.t2 = 0; 01342 } 01343 else 01344 { 01345 //T1 and T2 are provided as a hint 01346 iaNaOption.t1 = htonl(context->ia.t1); 01347 iaNaOption.t2 = htonl(context->ia.t2); 01348 } 01349 01350 //The client includes IA options for any IAs to which 01351 //it wants the server to assign addresses 01352 option = dhcpv6AddOption(message, &length, DHCPV6_OPTION_IA_NA, 01353 &iaNaOption, sizeof(Dhcpv6IaNaOption)); 01354 01355 //Request, Confirm, Renew, Rebind, Release or Decline message? 01356 if(type == DHCPV6_MSG_TYPE_REQUEST || 01357 type == DHCPV6_MSG_TYPE_CONFIRM || 01358 type == DHCPV6_MSG_TYPE_RENEW || 01359 type == DHCPV6_MSG_TYPE_REBIND || 01360 type == DHCPV6_MSG_TYPE_RELEASE || 01361 type == DHCPV6_MSG_TYPE_DECLINE) 01362 { 01363 //Loop through the IPv6 addresses recorded by the client 01364 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 01365 { 01366 //Point to the current entry 01367 entry = &context->ia.addrList[i]; 01368 01369 //Valid IPv6 address? 01370 if(entry->validLifetime > 0) 01371 { 01372 //Prepare an IA Address option 01373 iaAddrOption.address = entry->addr; 01374 01375 //Confirm message? 01376 if(type == DHCPV6_MSG_TYPE_CONFIRM) 01377 { 01378 //The client should set the preferred and valid lifetimes to 0 01379 iaAddrOption.preferredLifetime = 0; 01380 iaAddrOption.validLifetime = 0; 01381 } 01382 else 01383 { 01384 //Preferred and valid lifetimes are provided as a hint 01385 iaAddrOption.preferredLifetime = htonl(entry->preferredLifetime); 01386 iaAddrOption.validLifetime = htonl(entry->validLifetime); 01387 } 01388 01389 //Add the IA Address option 01390 dhcpv6AddSubOption(option, &length, DHCPV6_OPTION_IAADDR, 01391 &iaAddrOption, sizeof(iaAddrOption)); 01392 } 01393 } 01394 } 01395 01396 //Compute the time elapsed since the client sent the first message 01397 elapsedTimeOption.value = dhcpv6ClientComputeElapsedTime(context); 01398 01399 //The client must include an Elapsed Time option in messages to indicate 01400 //how long the client has been trying to complete a DHCP message exchange 01401 dhcpv6AddOption(message, &length, DHCPV6_OPTION_ELAPSED_TIME, 01402 &elapsedTimeOption, sizeof(Dhcpv6ElapsedTimeOption)); 01403 01404 //Adjust the length of the multi-part buffer 01405 netBufferSetLength(buffer, offset + length); 01406 01407 //Destination address 01408 destIpAddr.length = sizeof(Ipv6Addr); 01409 destIpAddr.ipv6Addr = DHCPV6_ALL_RELAY_AGENTS_AND_SERVERS_ADDR; 01410 01411 //Debug message 01412 TRACE_DEBUG("\r\n%s: Sending DHCPv6 message (%" PRIuSIZE " bytes)...\r\n", 01413 formatSystemTime(osGetSystemTime(), NULL), length); 01414 01415 //Dump the contents of the message for debugging purpose 01416 dhcpv6DumpMessage(message, length); 01417 01418 //Send DHCPv6 message 01419 error = udpSendDatagramEx(interface, DHCPV6_CLIENT_PORT, 01420 &destIpAddr, DHCPV6_SERVER_PORT, buffer, offset, 0); 01421 01422 //Free previously allocated memory 01423 netBufferFree(buffer); 01424 //Return status code 01425 return error; 01426 } 01427 01428 01429 /** 01430 * @brief Process incoming DHCPv6 message 01431 * @param[in] interface Underlying network interface 01432 * @param[in] pseudoHeader UDP pseudo header 01433 * @param[in] udpHeader UDP header 01434 * @param[in] buffer Multi-part buffer containing the incoming DHCPv6 message 01435 * @param[in] offset Offset to the first byte of the DHCPv6 message 01436 * @param[in] params Pointer to the DHCPv6 client context 01437 **/ 01438 01439 void dhcpv6ClientProcessMessage(NetInterface *interface, 01440 const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, 01441 const NetBuffer *buffer, size_t offset, void *params) 01442 { 01443 size_t length; 01444 Dhcpv6ClientContext *context; 01445 Dhcpv6Message *message; 01446 01447 //Point to the DHCPv6 client context 01448 context = (Dhcpv6ClientContext *) params; 01449 01450 //Retrieve the length of the DHCPv6 message 01451 length = netBufferGetLength(buffer) - offset; 01452 01453 //Make sure the DHCPv6 message is valid 01454 if(length < sizeof(Dhcpv6Message)) 01455 return; 01456 01457 //Point to the beginning of the DHCPv6 message 01458 message = netBufferAt(buffer, offset); 01459 //Sanity check 01460 if(message == NULL) 01461 return; 01462 01463 //Debug message 01464 TRACE_DEBUG("\r\n%s: DHCPv6 message received (%" PRIuSIZE " bytes)...\r\n", 01465 formatSystemTime(osGetSystemTime(), NULL), length); 01466 01467 //Dump the contents of the message for debugging purpose 01468 dhcpv6DumpMessage(message, length); 01469 01470 //Check message type 01471 switch(message->msgType) 01472 { 01473 case DHCPV6_MSG_TYPE_ADVERTISE: 01474 //Parse Advertise message 01475 dhcpv6ClientParseAdvertise(context, message, length); 01476 break; 01477 case DHCPV6_MSG_TYPE_REPLY: 01478 //Parse Reply message 01479 dhcpv6ClientParseReply(context, message, length); 01480 break; 01481 default: 01482 //Silently drop incoming message 01483 break; 01484 } 01485 } 01486 01487 01488 /** 01489 * @brief Parse Advertise message 01490 * @param[in] context Pointer to the DHCPv6 client context 01491 * @param[in] message Pointer to the incoming message to parse 01492 * @param[in] length Length of the incoming message 01493 **/ 01494 01495 void dhcpv6ClientParseAdvertise(Dhcpv6ClientContext *context, 01496 const Dhcpv6Message *message, size_t length) 01497 { 01498 uint_t i; 01499 int_t serverPreference; 01500 NetInterface *interface; 01501 Dhcpv6StatusCode status; 01502 Dhcpv6Option *option; 01503 Dhcpv6Option *serverIdOption; 01504 Dhcpv6IaNaOption *iaNaOption; 01505 01506 //Point to the underlying network interface 01507 interface = context->settings.interface; 01508 01509 //Make sure that the Advertise message is received in response to 01510 //a Solicit message 01511 if(context->state != DHCPV6_STATE_SOLICIT) 01512 return; 01513 01514 //Discard any received packet that does not match the transaction ID 01515 if(LOAD24BE(message->transactionId) != context->transactionId) 01516 return; 01517 01518 //Get the length of the Options field 01519 length -= sizeof(Dhcpv6Message); 01520 01521 //Search for the Client Identifier option 01522 option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID); 01523 01524 //Discard any received packet that does not include a Client Identifier option 01525 if(option == NULL) 01526 return; 01527 //Check the length of the option 01528 if(ntohs(option->length) != context->clientIdLength) 01529 return; 01530 //Check whether the Client Identifier matches our identifier 01531 if(memcmp(option->value, context->clientId, context->clientIdLength)) 01532 return; 01533 01534 //Search for the Server Identifier option 01535 serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID); 01536 01537 //Discard any received packet that does not include a Server Identifier option 01538 if(serverIdOption == NULL) 01539 return; 01540 //Check the length of the server DUID 01541 if(ntohs(serverIdOption->length) == 0) 01542 return; 01543 if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE) 01544 return; 01545 01546 //Get the status code returned by the server 01547 status = dhcpv6GetStatusCode(message->options, length); 01548 01549 //If the message contains a Status Code option indicating a failure, 01550 //then the Advertise message is discarded by the client 01551 if(status != DHCPV6_STATUS_SUCCESS) 01552 return; 01553 01554 //Search for the Preference option 01555 option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_PREFERENCE); 01556 01557 //Check whether the option has been found 01558 if(option != NULL && ntohs(option->length) == sizeof(Dhcpv6PreferenceOption)) 01559 { 01560 //Server server preference value 01561 serverPreference = option->value[0]; 01562 } 01563 else 01564 { 01565 //Any Advertise that does not include a Preference option 01566 //is considered to have a preference value of 0 01567 serverPreference = 0; 01568 } 01569 01570 //Select the Advertise message that offers the highest server preference value 01571 if(serverPreference > context->serverPreference) 01572 { 01573 //Save the length of the DUID 01574 context->serverIdLength = ntohs(serverIdOption->length); 01575 //Record the server DUID 01576 memcpy(context->serverId, serverIdOption->value, context->serverIdLength); 01577 01578 //Flush the list of IPv6 addresses from the client's IA 01579 dhcpv6ClientFlushAddrList(context); 01580 } 01581 01582 //Point to the first option 01583 i = 0; 01584 01585 //Loop through DHCPv6 options 01586 while(i < length) 01587 { 01588 //Search for an IA_NA option 01589 option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA); 01590 01591 //Unable to find the specified option? 01592 if(option == NULL) 01593 break; 01594 01595 //Make sure the IA_NA option is valid 01596 if(ntohs(option->length) >= sizeof(Dhcpv6IaNaOption)) 01597 { 01598 //Get the parameters associated with the IA_NA 01599 iaNaOption = (Dhcpv6IaNaOption *) option->value; 01600 01601 //Check the IA identifier 01602 if(ntohl(iaNaOption->iaId) == interface->id) 01603 { 01604 //The client examines the status code in each IA individually 01605 status = dhcpv6GetStatusCode(iaNaOption->options, 01606 ntohs(option->length) - sizeof(Dhcpv6IaNaOption)); 01607 01608 //The client must ignore any Advertise message that includes a Status 01609 //Code option containing the value NoAddrsAvail 01610 if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE) 01611 return; 01612 } 01613 01614 //Check the server preference value 01615 if(serverPreference > context->serverPreference) 01616 { 01617 //Parse the contents of the IA_NA option 01618 dhcpv6ClientParseIaNaOption(context, option); 01619 } 01620 } 01621 01622 //Jump to the next option 01623 i += sizeof(Dhcpv6Option) + ntohs(option->length); 01624 } 01625 01626 //Record the highest server preference value 01627 if(serverPreference > context->serverPreference) 01628 context->serverPreference = serverPreference; 01629 01630 //If the client receives an Advertise message that includes a 01631 //Preference option with a preference value of 255, the client 01632 //immediately completes the message exchange 01633 if(serverPreference == DHCPV6_MAX_SERVER_PREFERENCE) 01634 { 01635 //Continue configuration procedure 01636 dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); 01637 } 01638 //The message exchange is not terminated before the first RT has elapsed 01639 else if(context->retransmitCount > 1) 01640 { 01641 //Continue configuration procedure 01642 dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); 01643 } 01644 } 01645 01646 01647 /** 01648 * @brief Parse Reply message 01649 * @param[in] context Pointer to the DHCPv6 client context 01650 * @param[in] message Pointer to the incoming message to parse 01651 * @param[in] length Length of the incoming message 01652 **/ 01653 01654 void dhcpv6ClientParseReply(Dhcpv6ClientContext *context, 01655 const Dhcpv6Message *message, size_t length) 01656 { 01657 error_t error; 01658 uint_t i; 01659 uint_t k; 01660 uint_t n; 01661 bool_t iaNaOptionFound; 01662 systime_t minPreferredLifetime; 01663 NetInterface *interface; 01664 Dhcpv6StatusCode status; 01665 Dhcpv6Option *option; 01666 Dhcpv6Option *serverIdOption; 01667 Dhcpv6ClientAddrEntry *entry; 01668 01669 //Point to the underlying network interface 01670 interface = context->settings.interface; 01671 01672 //Discard any received packet that does not match the transaction ID 01673 if(LOAD24BE(message->transactionId) != context->transactionId) 01674 return; 01675 01676 //Get the length of the Options field 01677 length -= sizeof(Dhcpv6Message); 01678 01679 //Search for the Client Identifier option 01680 option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_CLIENTID); 01681 01682 //Discard any received packet that does not include a Client Identifier option 01683 if(option == NULL) 01684 return; 01685 //Check the length of the option 01686 if(ntohs(option->length) != context->clientIdLength) 01687 return; 01688 //Check whether the Client Identifier matches our identifier 01689 if(memcmp(option->value, context->clientId, context->clientIdLength)) 01690 return; 01691 01692 //Search for the Server Identifier option 01693 serverIdOption = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_SERVERID); 01694 01695 //Discard any received packet that does not include a Server Identifier option 01696 if(serverIdOption == NULL) 01697 return; 01698 //Check the length of the server DUID 01699 if(ntohs(serverIdOption->length) == 0) 01700 return; 01701 if(ntohs(serverIdOption->length) > DHCPV6_MAX_DUID_SIZE) 01702 return; 01703 01704 //Get the status code returned by the server 01705 status = dhcpv6GetStatusCode(message->options, length); 01706 01707 //Check current state 01708 if(context->state == DHCPV6_STATE_SOLICIT) 01709 { 01710 //A Reply message is not acceptable when rapid commit is disallowed 01711 if(!context->settings.rapidCommit) 01712 return; 01713 01714 //Search for the Rapid Commit option 01715 option = dhcpv6GetOption(message->options, length, DHCPV6_OPTION_RAPID_COMMIT); 01716 01717 //The client discards any message that does not include a Rapid Commit option 01718 if(option == NULL || ntohs(option->length) != 0) 01719 return; 01720 } 01721 else if(context->state == DHCPV6_STATE_REQUEST) 01722 { 01723 //The client must discard the Reply message if the contents of the 01724 //Server Identifier option do not match the server’s DUID 01725 if(!dhcpv6ClientCheckServerId(context, serverIdOption)) 01726 return; 01727 } 01728 else if(context->state == DHCPV6_STATE_CONFIRM) 01729 { 01730 //When the client receives a NotOnLink status from the server in response 01731 //to a Confirm message, the client performs DHCP server solicitation 01732 if(status == DHCPV6_STATUS_NOT_ON_LINK) 01733 { 01734 //Restart the DHCP server discovery process 01735 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01736 01737 //Exit immediately 01738 return; 01739 } 01740 } 01741 else if(context->state == DHCPV6_STATE_RENEW) 01742 { 01743 //The client must discard the Reply message if the contents of the 01744 //Server Identifier option do not match the server’s DUID 01745 if(!dhcpv6ClientCheckServerId(context, serverIdOption)) 01746 return; 01747 } 01748 else if(context->state == DHCPV6_STATE_REBIND) 01749 { 01750 //Do not check the server's DUID when the Reply message is 01751 //received in response to a Rebind message 01752 } 01753 else if(context->state == DHCPV6_STATE_RELEASE) 01754 { 01755 //The client must discard the Reply message if the contents of the 01756 //Server Identifier option do not match the server’s DUID 01757 if(!dhcpv6ClientCheckServerId(context, serverIdOption)) 01758 return; 01759 01760 //When the client receives a valid Reply message in response to a 01761 //Release message, the client considers the Release event completed, 01762 //regardless of the Status Code option(s) returned by the server 01763 context->running = FALSE; 01764 01765 //Reinitialize state machine 01766 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01767 01768 //Exit immediately 01769 return; 01770 } 01771 else if(context->state == DHCPV6_STATE_DECLINE) 01772 { 01773 //The client must discard the Reply message if the contents of the 01774 //Server Identifier option do not match the server’s DUID 01775 if(!dhcpv6ClientCheckServerId(context, serverIdOption)) 01776 return; 01777 01778 //When the client receives a valid Reply message in response to a 01779 //Decline message, the client considers the Decline event completed, 01780 //regardless of the Status Code option returned by the server 01781 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01782 01783 //Exit immediately 01784 return; 01785 } 01786 else 01787 { 01788 //Silently discard the Reply message 01789 return; 01790 } 01791 01792 //Check status code 01793 if(status == DHCPV6_STATUS_USE_MULTICAST) 01794 { 01795 //When the client receives a Reply message with a Status Code option 01796 //with the value UseMulticast, the client records the receipt of the 01797 //message and sends subsequent messages to the server through the 01798 //interface on which the message was received using multicast 01799 return; 01800 } 01801 else if(status == DHCPV6_STATUS_UNSPEC_FAILURE) 01802 { 01803 //If the client receives a Reply message with a Status Code containing 01804 //UnspecFail, the server is indicating that it was unable to process 01805 //the message due to an unspecified failure condition 01806 return; 01807 } 01808 01809 //This flag will be set if a valid IA_NA option is found 01810 iaNaOptionFound = FALSE; 01811 //Point to the first option 01812 i = 0; 01813 01814 //Loop through DHCPv6 options 01815 while(i < length) 01816 { 01817 //Search for an IA_NA option 01818 option = dhcpv6GetOption(message->options + i, length - i, DHCPV6_OPTION_IA_NA); 01819 01820 //Unable to find the specified option? 01821 if(option == NULL) 01822 break; 01823 01824 //Parse the contents of the IA_NA option 01825 error = dhcpv6ClientParseIaNaOption(context, option); 01826 01827 //Check error code 01828 if(error == NO_ERROR) 01829 { 01830 //A valid IA_NA option has been found 01831 iaNaOptionFound = TRUE; 01832 } 01833 else if(error == ERROR_NOT_ON_LINK) 01834 { 01835 //When the client receives a NotOnLink status from the server 01836 //in response to a Request, the client can either re-issue the 01837 //Request without specifying any addresses or restart the DHCP 01838 //server discovery process 01839 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01840 01841 //Exit immediately 01842 return; 01843 } 01844 else if(error == ERROR_NO_BINDING) 01845 { 01846 //When the client receives a Reply message in response to a Renew 01847 //or Rebind message, the client sends a Request message if any of 01848 //the IAs in the Reply message contains the NoBinding status code 01849 dhcpv6ClientChangeState(context, DHCPV6_STATE_REQUEST, 0); 01850 01851 //Exit immediately 01852 return; 01853 } 01854 else 01855 { 01856 //If an invalid option is received, the client discards 01857 //the option and process the rest of the message... 01858 } 01859 01860 //Jump to the next option 01861 i += sizeof(Dhcpv6Option) + ntohs(option->length); 01862 } 01863 01864 //No usable addresses in any of the IAs? 01865 if(!iaNaOptionFound) 01866 { 01867 //Check whether the client receives a Reply message in response 01868 //to a Renew or Rebind message 01869 if(context->state == DHCPV6_STATE_RENEW || 01870 context->state == DHCPV6_STATE_REBIND) 01871 { 01872 //The client sends a Renew/Rebind if the IA is not in the Reply message 01873 } 01874 else 01875 { 01876 //If the client finds no usable addresses in any of the IAs, it may try 01877 //another server (perhaps restarting the DHCP server discovery process) 01878 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01879 } 01880 01881 //Exit immediately 01882 return; 01883 } 01884 01885 //Total number of valid IPv6 in the IA 01886 n = 0; 01887 //Number of new IPv6 addresses in the IA 01888 k = 0; 01889 //Minimum preferred lifetime observed in the IA 01890 minPreferredLifetime = 0; 01891 01892 //Loop through the IPv6 addresses recorded by the DHCPv6 client 01893 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 01894 { 01895 //Point to the current entry 01896 entry = &context->ia.addrList[i]; 01897 01898 //Valid IPv6 address? 01899 if(entry->validLifetime > 0) 01900 { 01901 //Total number of valid IPv6 in the IA 01902 n++; 01903 01904 //Save the minimum preferred lifetime that has been observed so far 01905 if(minPreferredLifetime < entry->preferredLifetime) 01906 minPreferredLifetime = entry->preferredLifetime; 01907 01908 //Update lifetimes of the current IPv6 address 01909 ipv6AddAddr(interface, &entry->addr, entry->validLifetime, 01910 entry->preferredLifetime); 01911 01912 //New IPv6 address added? 01913 if(ipv6GetAddrState(interface, &entry->addr) == IPV6_ADDR_STATE_TENTATIVE) 01914 k++; 01915 } 01916 } 01917 01918 //Make sure that the IA contains at least one IPv6 address 01919 if(n > 0) 01920 { 01921 //Save the length of the DUID 01922 context->serverIdLength = ntohs(serverIdOption->length); 01923 //Record the server DUID 01924 memcpy(context->serverId, serverIdOption->value, context->serverIdLength); 01925 01926 //Save the time a which the lease was obtained 01927 context->leaseStartTime = osGetSystemTime(); 01928 01929 //Check the value of T1 01930 if(context->ia.t1 == 0) 01931 { 01932 //If T1 is set to 0 by the server, the client may send 01933 //a Renew message at the client's discretion 01934 if(minPreferredLifetime == DHCPV6_INFINITE_TIME) 01935 context->ia.t1 = DHCPV6_INFINITE_TIME; 01936 else 01937 context->ia.t1 = minPreferredLifetime / 2; 01938 } 01939 01940 //Check the value of T2 01941 if(context->ia.t2 == 0) 01942 { 01943 //If T2 is set to 0 by the server, the client may send 01944 //a Rebind message at the client's discretion 01945 if(context->ia.t1 == DHCPV6_INFINITE_TIME) 01946 context->ia.t2 = DHCPV6_INFINITE_TIME; 01947 else 01948 context->ia.t2 = context->ia.t1 + context->ia.t1 / 2; 01949 } 01950 01951 //Any addresses added in the IA? 01952 if(k > 0) 01953 { 01954 //Perform Duplicate Address Detection for the new IPv6 addresses 01955 dhcpv6ClientChangeState(context, DHCPV6_STATE_DAD, 0); 01956 } 01957 else 01958 { 01959 //Switch to the BOUND state 01960 dhcpv6ClientChangeState(context, DHCPV6_STATE_BOUND, 0); 01961 } 01962 } 01963 else 01964 { 01965 //If the client finds no usable addresses in any of the IAs, it may try 01966 //another server (perhaps restarting the DHCP server discovery process) 01967 dhcpv6ClientChangeState(context, DHCPV6_STATE_INIT, 0); 01968 } 01969 } 01970 01971 01972 /** 01973 * @brief Parse IA_NA option 01974 * @param[in] context Pointer to the DHCPv6 client context 01975 * @param[in] option Pointer to the IA_NA option to parse 01976 * @return Error code 01977 **/ 01978 01979 error_t dhcpv6ClientParseIaNaOption(Dhcpv6ClientContext *context, 01980 const Dhcpv6Option *option) 01981 { 01982 error_t error; 01983 uint_t n; 01984 size_t i; 01985 size_t length; 01986 NetInterface *interface; 01987 Dhcpv6StatusCode status; 01988 Dhcpv6IaNaOption *iaNaOption; 01989 01990 //Point to the underlying network interface 01991 interface = context->settings.interface; 01992 01993 //Number of addresses found in the IA_NA option 01994 n = 0; 01995 01996 //Make sure the IA_NA option is valid 01997 if(ntohs(option->length) < sizeof(Dhcpv6IaNaOption)) 01998 return ERROR_INVALID_LENGTH; 01999 02000 //Get the parameters associated with the IA_NA 02001 iaNaOption = (Dhcpv6IaNaOption *) option->value; 02002 //Compute the length of IA_NA Options field 02003 length = ntohs(option->length) - sizeof(Dhcpv6IaNaOption); 02004 02005 //Check the IA identifier 02006 if(ntohl(iaNaOption->iaId) != interface->id) 02007 return ERROR_WRONG_IDENTIFIER; 02008 02009 //If a client receives an IA_NA with T1 greater than T2, and both T1 02010 //and T2 are greater than 0, the client discards the IA_NA option and 02011 //processes the remainder of the message as though the server had not 02012 //included the invalid IA_NA option 02013 if(ntohl(iaNaOption->t1) > ntohl(iaNaOption->t2) && ntohl(iaNaOption->t2) > 0) 02014 return ERROR_INVALID_PARAMETER; 02015 02016 //The client examines the status code in each IA individually 02017 status = dhcpv6GetStatusCode(iaNaOption->options, length); 02018 02019 //Check error code 02020 if(status == DHCPV6_STATUS_NO_ADDRS_AVAILABLE) 02021 { 02022 //The client has received no usable address in the IA 02023 return ERROR_NO_ADDRESS; 02024 } 02025 else if(status == DHCPV6_STATUS_NO_BINDING) 02026 { 02027 //Client record (binding) unavailable 02028 return ERROR_NO_BINDING; 02029 } 02030 else if(status == DHCPV6_STATUS_NOT_ON_LINK) 02031 { 02032 //The prefix for the address is not appropriate for the 02033 //link to which the client is attached 02034 return ERROR_NOT_ON_LINK; 02035 } 02036 else if(status != DHCPV6_STATUS_SUCCESS) 02037 { 02038 //Failure, reason unspecified 02039 return ERROR_FAILURE; 02040 } 02041 02042 //Record T1 and T2 times 02043 context->ia.t1 = ntohl(iaNaOption->t1); 02044 context->ia.t2 = ntohl(iaNaOption->t2); 02045 02046 //Point to the first option 02047 i = 0; 02048 02049 //Loop through IA_NA options 02050 while(i < length) 02051 { 02052 //Search for an IA Address option 02053 option = dhcpv6GetOption(iaNaOption->options + i, length - i, DHCPV6_OPTION_IAADDR); 02054 02055 //Unable to find the specified option? 02056 if(option == NULL) 02057 break; 02058 02059 //Parse the contents of the IA Address option 02060 error = dhcpv6ClientParseIaAddrOption(context, option); 02061 02062 //Check status code 02063 if(!error) 02064 { 02065 //Increment the number of addresses found in the IA_NA option 02066 n++; 02067 } 02068 02069 //Jump to the next option 02070 i += sizeof(Dhcpv6Option) + ntohs(option->length); 02071 } 02072 02073 //No usable addresses in the IA_NA option? 02074 if(n == 0) 02075 { 02076 //Report an error 02077 return ERROR_NO_ADDRESS; 02078 } 02079 02080 //Successful processing 02081 return NO_ERROR; 02082 } 02083 02084 02085 /** 02086 * @brief Parse IA Address option 02087 * @param[in] context Pointer to the DHCPv6 client context 02088 * @param[in] option Pointer to the IA Address option to parse 02089 * @return Error code 02090 **/ 02091 02092 error_t dhcpv6ClientParseIaAddrOption(Dhcpv6ClientContext *context, 02093 const Dhcpv6Option *option) 02094 { 02095 size_t length; 02096 uint32_t validLifetime; 02097 uint32_t preferredLifetime; 02098 Dhcpv6StatusCode status; 02099 Dhcpv6IaAddrOption *iaAddrOption; 02100 02101 //Make sure the IA Address option is valid 02102 if(ntohs(option->length) < sizeof(Dhcpv6IaAddrOption)) 02103 return ERROR_INVALID_LENGTH; 02104 02105 //Point to the contents of the IA Address option 02106 iaAddrOption = (Dhcpv6IaAddrOption *) option->value; 02107 //Compute the length of IA Address Options field 02108 length = ntohs(option->length) - sizeof(Dhcpv6IaAddrOption); 02109 02110 //Convert lifetimes to host byte order 02111 validLifetime = ntohl(iaAddrOption->validLifetime); 02112 preferredLifetime = ntohl(iaAddrOption->preferredLifetime); 02113 02114 //A client discards any addresses for which the preferred lifetime 02115 //is greater than the valid lifetime 02116 if(preferredLifetime > validLifetime) 02117 return ERROR_INVALID_PARAMETER; 02118 02119 //The client examines the status code in each IA Address 02120 status = dhcpv6GetStatusCode(iaAddrOption->options, length); 02121 02122 //Any error to report? 02123 if(status != DHCPV6_STATUS_SUCCESS) 02124 return ERROR_FAILURE; 02125 02126 //Check the value of the Valid Lifetime 02127 if(iaAddrOption->validLifetime > 0) 02128 { 02129 //Add any new addresses in the IA option to the IA as recorded 02130 //by the client 02131 dhcpv6ClientAddAddr(context, &iaAddrOption->address, 02132 validLifetime, preferredLifetime); 02133 } 02134 else 02135 { 02136 //Discard any addresses from the IA, as recorded by the client, 02137 //that have a valid lifetime of 0 in the IA Address option 02138 dhcpv6ClientRemoveAddr(context, &iaAddrOption->address); 02139 } 02140 02141 //Successful processing 02142 return NO_ERROR; 02143 } 02144 02145 02146 /** 02147 * @brief Add an IPv6 address to the IA 02148 * @param[in] context Pointer to the DHCPv6 client context 02149 * @param[in] addr IPv6 address to be added 02150 * @param[in] validLifetime Valid lifetime, in seconds 02151 * @param[in] preferredLifetime Preferred lifetime, in seconds 02152 **/ 02153 02154 void dhcpv6ClientAddAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr, 02155 uint32_t validLifetime, uint32_t preferredLifetime) 02156 { 02157 uint_t i; 02158 Dhcpv6ClientAddrEntry *entry; 02159 Dhcpv6ClientAddrEntry *firstFreeEntry; 02160 02161 //Keep track of the first free entry 02162 firstFreeEntry = NULL; 02163 02164 //Loop through the IPv6 addresses recorded by the DHCPv6 client 02165 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 02166 { 02167 //Point to the current entry 02168 entry = &context->ia.addrList[i]; 02169 02170 //Valid IPv6 address? 02171 if(entry->validLifetime > 0) 02172 { 02173 //Check whether the current entry matches the specified address 02174 if(ipv6CompAddr(&entry->addr, addr)) 02175 break; 02176 } 02177 else 02178 { 02179 //Keep track of the first free entry 02180 if(firstFreeEntry == NULL) 02181 firstFreeEntry = entry; 02182 } 02183 } 02184 02185 //No matching entry found? 02186 if(i >= IPV6_PREFIX_LIST_SIZE) 02187 entry = firstFreeEntry; 02188 02189 //Update the entry if necessary 02190 if(entry != NULL) 02191 { 02192 //Save IPv6 address 02193 entry->addr = *addr; 02194 02195 //Save lifetimes 02196 entry->validLifetime = validLifetime; 02197 entry->preferredLifetime = preferredLifetime; 02198 } 02199 } 02200 02201 02202 /** 02203 * @brief Remove an IPv6 address from the IA 02204 * @param[in] context Pointer to the DHCPv6 client context 02205 * @param[in] addr IPv6 address to be removed 02206 **/ 02207 02208 void dhcpv6ClientRemoveAddr(Dhcpv6ClientContext *context, const Ipv6Addr *addr) 02209 { 02210 uint_t i; 02211 NetInterface *interface; 02212 Dhcpv6ClientAddrEntry *entry; 02213 02214 //Point to the underlying network interface 02215 interface = context->settings.interface; 02216 02217 //Loop through the IPv6 addresses recorded by the DHCPv6 client 02218 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 02219 { 02220 //Point to the current entry 02221 entry = &context->ia.addrList[i]; 02222 02223 //Valid IPv6 address? 02224 if(entry->validLifetime > 0) 02225 { 02226 //Check whether the current entry matches the specified address 02227 if(ipv6CompAddr(&entry->addr, addr)) 02228 { 02229 //The IPv6 address is no more valid and should be removed from 02230 //the list of IPv6 addresses assigned to the interface 02231 ipv6RemoveAddr(interface, addr); 02232 02233 //Remove the IPv6 address from the IA 02234 entry->validLifetime = 0; 02235 } 02236 } 02237 } 02238 } 02239 02240 02241 /** 02242 * @brief Flush the list of IPv6 addresses from the IA 02243 * @param[in] context Pointer to the DHCPv6 client context 02244 **/ 02245 02246 void dhcpv6ClientFlushAddrList(Dhcpv6ClientContext *context) 02247 { 02248 uint_t i; 02249 NetInterface *interface; 02250 Dhcpv6ClientAddrEntry *entry; 02251 02252 //Point to the underlying network interface 02253 interface = context->settings.interface; 02254 02255 //Loop through the IPv6 addresses recorded by the DHCPv6 client 02256 for(i = 0; i < DHCPV6_CLIENT_ADDR_LIST_SIZE; i++) 02257 { 02258 //Point to the current entry 02259 entry = &context->ia.addrList[i]; 02260 02261 //Valid IPv6 address? 02262 if(entry->validLifetime > 0) 02263 { 02264 //The IPv6 address is no more valid and should be removed from 02265 //the list of IPv6 addresses assigned to the interface 02266 ipv6RemoveAddr(interface, &entry->addr); 02267 02268 //Remove the IPv6 address from the IA 02269 entry->validLifetime = 0; 02270 } 02271 } 02272 } 02273 02274 02275 /** 02276 * @brief Generate client's DUID 02277 * @param[in] context Pointer to the DHCPv6 client context 02278 * @return Error code 02279 **/ 02280 02281 error_t dhcpv6ClientGenerateDuid(Dhcpv6ClientContext *context) 02282 { 02283 NetInterface *interface; 02284 Dhcpv6DuidLl *duid; 02285 02286 //Point to the underlying network interface 02287 interface = context->settings.interface; 02288 02289 //Point to the buffer where to format the client's DUID 02290 duid = (Dhcpv6DuidLl *) context->clientId; 02291 02292 #if (ETH_SUPPORT == ENABLED) 02293 //Generate a DUID-LL from the MAC address 02294 duid->type = HTONS(DHCPV6_DUID_LL); 02295 duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_ETH); 02296 duid->linkLayerAddr = interface->macAddr; 02297 #else 02298 //Generate a DUID-LL from the EUI-64 identifier 02299 duid->type = HTONS(DHCPV6_DUID_LL); 02300 duid->hardwareType = HTONS(DHCPV6_HARDWARE_TYPE_EUI64); 02301 duid->linkLayerAddr = interface->eui64; 02302 #endif 02303 02304 //Length of the newly generated DUID 02305 context->clientIdLength = sizeof(Dhcpv6DuidLl); 02306 02307 //Successful processing 02308 return NO_ERROR; 02309 } 02310 02311 02312 /** 02313 * @brief Generate client's fully qualified domain name 02314 * @param[in] context Pointer to the DHCPv6 client context 02315 * @return Error code 02316 **/ 02317 02318 error_t dhcpv6ClientGenerateFqdn(Dhcpv6ClientContext *context) 02319 { 02320 NetInterface *interface; 02321 Dhcpv6FqdnOption *fqdnOption; 02322 02323 //Point to the underlying network interface 02324 interface = context->settings.interface; 02325 02326 //Point to the buffer where to format the client's FQDN 02327 fqdnOption = (Dhcpv6FqdnOption *) context->clientFqdn; 02328 02329 //Set flags 02330 fqdnOption->mbz = 0; 02331 fqdnOption->n = FALSE; 02332 fqdnOption->o = FALSE; 02333 fqdnOption->s = FALSE; 02334 02335 //Encode client's FQDN 02336 context->clientFqdnLength = dnsEncodeName(interface->hostname, 02337 fqdnOption->domainName); 02338 02339 //Successful processing 02340 return NO_ERROR; 02341 } 02342 02343 02344 /** 02345 * @brief Generate a link-local address 02346 * @param[in] context Pointer to the DHCPv6 client context 02347 * @return Error code 02348 **/ 02349 02350 error_t dhcpv6ClientGenerateLinkLocalAddr(Dhcpv6ClientContext *context) 02351 { 02352 error_t error; 02353 NetInterface *interface; 02354 Ipv6Addr addr; 02355 02356 //Point to the underlying network interface 02357 interface = context->settings.interface; 02358 02359 //Check whether a link-local address has been manually assigned 02360 if(interface->ipv6Context.addrList[0].state != IPV6_ADDR_STATE_INVALID && 02361 interface->ipv6Context.addrList[0].permanent) 02362 { 02363 //Keep using the current link-local address 02364 error = NO_ERROR; 02365 } 02366 else 02367 { 02368 //A link-local address is formed by combining the well-known 02369 //link-local prefix fe80::/10 with the interface identifier 02370 ipv6GenerateLinkLocalAddr(&interface->eui64, &addr); 02371 02372 #if (NDP_SUPPORT == ENABLED) 02373 //Check whether Duplicate Address Detection should be performed 02374 if(interface->ndpContext.dupAddrDetectTransmits > 0) 02375 { 02376 //Use the link-local address as a tentative address 02377 error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_TENTATIVE, 02378 NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE); 02379 } 02380 else 02381 #endif 02382 { 02383 //The use of the link-local address is now unrestricted 02384 error = ipv6SetAddr(interface, 0, &addr, IPV6_ADDR_STATE_PREFERRED, 02385 NDP_INFINITE_LIFETIME, NDP_INFINITE_LIFETIME, FALSE); 02386 } 02387 } 02388 02389 //Return status code 02390 return error; 02391 } 02392 02393 02394 /** 02395 * @brief Check the Server Identifier option 02396 * @param[in] context Pointer to the DHCPv6 client context 02397 * @param[in] serverIdOption Pointer to the Server Identifier option 02398 * @return TRUE if the option matches the server’s DUID, else FALSE 02399 **/ 02400 02401 bool_t dhcpv6ClientCheckServerId(Dhcpv6ClientContext *context, 02402 Dhcpv6Option *serverIdOption) 02403 { 02404 bool_t valid = FALSE; 02405 02406 //Check the length of the Server Identifier option 02407 if(ntohs(serverIdOption->length) == context->serverIdLength) 02408 { 02409 //Check whether the Server Identifier option matches the server’s DUID 02410 if(!memcmp(serverIdOption->value, context->serverId, context->serverIdLength)) 02411 valid = TRUE; 02412 } 02413 02414 //Return TRUE if the option matches the server’s DUID 02415 return valid; 02416 } 02417 02418 02419 /** 02420 * @brief Manage DHCPv6 configuration timeout 02421 * @param[in] context Pointer to the DHCPv6 client context 02422 **/ 02423 02424 void dhcpv6ClientCheckTimeout(Dhcpv6ClientContext *context) 02425 { 02426 systime_t time; 02427 NetInterface *interface; 02428 02429 //Point to the underlying network interface 02430 interface = context->settings.interface; 02431 02432 //Get current time 02433 time = osGetSystemTime(); 02434 02435 //Any registered callback? 02436 if(context->settings.timeoutEvent != NULL) 02437 { 02438 //DHCPv6 configuration timeout? 02439 if(timeCompare(time, context->configStartTime + context->settings.timeout) >= 0) 02440 { 02441 //Ensure the callback function is only called once 02442 if(!context->timeoutEventDone) 02443 { 02444 //Release exclusive access 02445 osReleaseMutex(&netMutex); 02446 //Invoke user callback function 02447 context->settings.timeoutEvent(context, interface); 02448 //Get exclusive access 02449 osAcquireMutex(&netMutex); 02450 02451 //Set flag 02452 context->timeoutEventDone = TRUE; 02453 } 02454 } 02455 } 02456 } 02457 02458 02459 /** 02460 * @brief Compute the time elapsed since the client sent the first message 02461 * @param[in] context Pointer to the DHCPv6 client context 02462 * @return The elapsed time expressed in hundredths of a second 02463 **/ 02464 02465 uint16_t dhcpv6ClientComputeElapsedTime(Dhcpv6ClientContext *context) 02466 { 02467 systime_t time; 02468 02469 //Check retransmission counter 02470 if(context->retransmitCount == 0) 02471 { 02472 //The elapsed time must be 0 for the first message 02473 time = 0; 02474 } 02475 else 02476 { 02477 //Compute the time elapsed since the client sent the 02478 //first message (in hundredths of a second) 02479 time = (osGetSystemTime() - context->exchangeStartTime) / 10; 02480 02481 //The value 0xFFFF is used to represent any elapsed time values 02482 //greater than the largest time value that can be represented 02483 time = MIN(time, 0xFFFF); 02484 } 02485 02486 //Convert the 16-bit value to network byte order 02487 return htons(time); 02488 } 02489 02490 02491 /** 02492 * @brief Update DHCPv6 FSM state 02493 * @param[in] context Pointer to the DHCPv6 client context 02494 * @param[in] newState New DHCPv6 state to switch to 02495 * @param[in] delay Initial delay 02496 **/ 02497 02498 void dhcpv6ClientChangeState(Dhcpv6ClientContext *context, 02499 Dhcpv6State newState, systime_t delay) 02500 { 02501 systime_t time; 02502 02503 //Get current time 02504 time = osGetSystemTime(); 02505 02506 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO) 02507 //Sanity check 02508 if(newState <= DHCPV6_STATE_DECLINE) 02509 { 02510 //DHCPv6 FSM states 02511 static const char_t *stateLabel[] = 02512 { 02513 "INIT", 02514 "SOLICIT", 02515 "REQUEST", 02516 "INIT-CONFIRM", 02517 "CONFIRM", 02518 "DAD", 02519 "BOUND", 02520 "RENEW", 02521 "REBIND", 02522 "RELEASE", 02523 "DECLINE" 02524 }; 02525 02526 //Debug message 02527 TRACE_INFO("%s: DHCPv6 client %s state\r\n", 02528 formatSystemTime(time, NULL), stateLabel[newState]); 02529 } 02530 #endif 02531 02532 //Set time stamp 02533 context->timestamp = time; 02534 //Set initial delay 02535 context->timeout = delay; 02536 //Reset retransmission counter 02537 context->retransmitCount = 0; 02538 //Switch to the new state 02539 context->state = newState; 02540 02541 //Any registered callback? 02542 if(context->settings.stateChangeEvent != NULL) 02543 { 02544 NetInterface *interface; 02545 02546 //Point to the underlying network interface 02547 interface = context->settings.interface; 02548 02549 //Release exclusive access 02550 osReleaseMutex(&netMutex); 02551 //Invoke user callback function 02552 context->settings.stateChangeEvent(context, interface, newState); 02553 //Get exclusive access 02554 osAcquireMutex(&netMutex); 02555 } 02556 } 02557 02558 02559 /** 02560 * @brief Dump DHCPv6 configuration for debugging purpose 02561 * @param[in] context Pointer to the DHCPv6 client context 02562 **/ 02563 02564 void dhcpv6ClientDumpConfig(Dhcpv6ClientContext *context) 02565 { 02566 #if (DHCPV6_TRACE_LEVEL >= TRACE_LEVEL_INFO) 02567 uint_t i; 02568 NetInterface *interface; 02569 Ipv6Context *ipv6Context; 02570 02571 //Point to the underlying network interface 02572 interface = context->settings.interface; 02573 //Point to the IPv6 context 02574 ipv6Context = &interface->ipv6Context; 02575 02576 //Debug message 02577 TRACE_INFO("\r\n"); 02578 TRACE_INFO("DHCPv6 configuration:\r\n"); 02579 02580 //Lease start time 02581 TRACE_INFO(" Lease Start Time = %s\r\n", 02582 formatSystemTime(context->leaseStartTime, NULL)); 02583 02584 //T1 parameter 02585 TRACE_INFO(" T1 = %" PRIu32 "s\r\n", context->ia.t1); 02586 //T2 parameter 02587 TRACE_INFO(" T2 = %" PRIu32 "s\r\n", context->ia.t2); 02588 02589 //Global addresses 02590 for(i = 1; i < IPV6_ADDR_LIST_SIZE; i++) 02591 { 02592 TRACE_INFO(" Global Address %u = %s\r\n", i, 02593 ipv6AddrToString(&ipv6Context->addrList[i].addr, NULL)); 02594 } 02595 02596 //DNS servers 02597 for(i = 0; i < IPV6_DNS_SERVER_LIST_SIZE; i++) 02598 { 02599 TRACE_INFO(" DNS Server %u = %s\r\n", i + 1, 02600 ipv6AddrToString(&ipv6Context->dnsServerList[i], NULL)); 02601 } 02602 02603 //Debug message 02604 TRACE_INFO("\r\n"); 02605 #endif 02606 } 02607 02608 #endif 02609
Generated on Tue Jul 12 2022 17:10:13 by
