Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
dhcp_server.c
Go to the documentation of this file.
00001 /** 00002 * @file dhcp_server.c 00003 * @brief DHCP server (Dynamic Host Configuration Protocol) 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 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL DHCP_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include "core/net.h" 00034 #include "dhcp/dhcp_server.h" 00035 #include "dhcp/dhcp_common.h" 00036 #include "dhcp/dhcp_debug.h" 00037 #include "date_time.h" 00038 #include "debug.h" 00039 00040 //Check TCP/IP stack configuration 00041 #if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED) 00042 00043 //Tick counter to handle periodic operations 00044 systime_t dhcpServerTickCounter; 00045 00046 00047 /** 00048 * @brief Initialize settings with default values 00049 * @param[out] settings Structure that contains DHCP server settings 00050 **/ 00051 00052 void dhcpServerGetDefaultSettings(DhcpServerSettings *settings) 00053 { 00054 uint_t i; 00055 00056 //Use default interface 00057 settings->interface = netGetDefaultInterface(); 00058 00059 //Support for quick configuration using rapid commit 00060 settings->rapidCommit = FALSE; 00061 //Lease time, in seconds, assigned to the DHCP clients 00062 settings->leaseTime = DHCP_SERVER_DEFAULT_LEASE_TIME; 00063 00064 //Lowest and highest IP addresses in the pool that are available 00065 //for dynamic address assignment 00066 settings->ipAddrRangeMin = IPV4_UNSPECIFIED_ADDR; 00067 settings->ipAddrRangeMax = IPV4_UNSPECIFIED_ADDR; 00068 00069 //Subnet mask 00070 settings->subnetMask = IPV4_UNSPECIFIED_ADDR; 00071 //Default gateway 00072 settings->defaultGateway = IPV4_UNSPECIFIED_ADDR; 00073 00074 //DNS servers 00075 for(i = 0; i < DHCP_SERVER_MAX_DNS_SERVERS; i++) 00076 settings->dnsServer[i] = IPV4_UNSPECIFIED_ADDR; 00077 } 00078 00079 00080 /** 00081 * @brief DHCP server initialization 00082 * @param[in] context Pointer to the DHCP server context 00083 * @param[in] settings DHCP server specific settings 00084 * @return Error code 00085 **/ 00086 00087 error_t dhcpServerInit(DhcpServerContext *context, const DhcpServerSettings *settings) 00088 { 00089 error_t error; 00090 NetInterface *interface; 00091 00092 //Debug message 00093 TRACE_INFO("Initializing DHCP server...\r\n"); 00094 00095 //Ensure the parameters are valid 00096 if(context == NULL || settings == NULL) 00097 return ERROR_INVALID_PARAMETER; 00098 00099 //Valid network interface? 00100 if(settings->interface == NULL) 00101 return ERROR_INVALID_PARAMETER; 00102 00103 //Get exclusive access 00104 osAcquireMutex(&netMutex); 00105 00106 //Point to the underlying network interface 00107 interface = settings->interface; 00108 00109 //Clear the DHCP server context 00110 memset(context, 0, sizeof(DhcpServerContext)); 00111 //Save user settings 00112 context->settings = *settings; 00113 00114 //Next IP address that will be assigned by the DHCP server 00115 context->nextIpAddr = settings->ipAddrRangeMin; 00116 //DHCP server is currently suspended 00117 context->running = FALSE; 00118 00119 //Callback function to be called when a DHCP message is received 00120 error = udpAttachRxCallback(interface, DHCP_SERVER_PORT, 00121 dhcpServerProcessMessage, context); 00122 00123 //Check status code 00124 if(!error) 00125 { 00126 //Attach the DHCP server context to the network interface 00127 interface->dhcpServerContext = context; 00128 } 00129 00130 //Release exclusive access 00131 osReleaseMutex(&netMutex); 00132 00133 //Return status code 00134 return error; 00135 } 00136 00137 00138 /** 00139 * @brief Start DHCP server 00140 * @param[in] context Pointer to the DHCP server context 00141 * @return Error code 00142 **/ 00143 00144 error_t dhcpServerStart(DhcpServerContext *context) 00145 { 00146 //Check parameter 00147 if(context == NULL) 00148 return ERROR_INVALID_PARAMETER; 00149 00150 //Debug message 00151 TRACE_INFO("Starting DHCP server...\r\n"); 00152 00153 //Get exclusive access 00154 osAcquireMutex(&netMutex); 00155 //Start DHCP server 00156 context->running = TRUE; 00157 //Release exclusive access 00158 osReleaseMutex(&netMutex); 00159 00160 //Successful processing 00161 return NO_ERROR; 00162 } 00163 00164 00165 /** 00166 * @brief Stop DHCP server 00167 * @param[in] context Pointer to the DHCP server context 00168 * @return Error code 00169 **/ 00170 00171 error_t dhcpServerStop(DhcpServerContext *context) 00172 { 00173 //Check parameter 00174 if(context == NULL) 00175 return ERROR_INVALID_PARAMETER; 00176 00177 //Debug message 00178 TRACE_INFO("Stopping DHCP server...\r\n"); 00179 00180 //Get exclusive access 00181 osAcquireMutex(&netMutex); 00182 //Stop DHCP server 00183 context->running = FALSE; 00184 //Release exclusive access 00185 osReleaseMutex(&netMutex); 00186 00187 //Successful processing 00188 return NO_ERROR; 00189 } 00190 00191 00192 /** 00193 * @brief DHCP server timer handler 00194 * 00195 * This routine must be periodically called by the TCP/IP stack to 00196 * manage DHCP server operation 00197 * 00198 * @param[in] context Pointer to the DHCP server context 00199 **/ 00200 00201 void dhcpServerTick(DhcpServerContext *context) 00202 { 00203 uint_t i; 00204 systime_t time; 00205 systime_t leaseTime; 00206 DhcpServerBinding *binding; 00207 00208 //Make sure the DHCP server has been properly instantiated 00209 if(context == NULL) 00210 return; 00211 00212 //Get current time 00213 time = osGetSystemTime(); 00214 00215 //Convert the lease time to milliseconds 00216 if(context->settings.leaseTime < (MAX_DELAY / 1000)) 00217 leaseTime = context->settings.leaseTime * 1000; 00218 else 00219 leaseTime = MAX_DELAY; 00220 00221 //Loop through the list of bindings 00222 for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++) 00223 { 00224 //Point to the current binding 00225 binding = &context->clientBinding[i]; 00226 00227 //Valid binding? 00228 if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR)) 00229 { 00230 //Check whether the network address has been committed 00231 if(binding->validLease) 00232 { 00233 //Check whether the lease has expired 00234 if(timeCompare(time, binding->timestamp + leaseTime) >= 0) 00235 { 00236 //The address lease is not more valid 00237 binding->validLease = FALSE; 00238 } 00239 } 00240 } 00241 } 00242 } 00243 00244 00245 /** 00246 * @brief Process incoming DHCP message 00247 * @param[in] interface Underlying network interface 00248 * @param[in] pseudoHeader UDP pseudo header 00249 * @param[in] udpHeader UDP header 00250 * @param[in] buffer Multi-part buffer containing the incoming DHCP message 00251 * @param[in] offset Offset to the first byte of the DHCP message 00252 * @param[in] params Pointer to the DHCP server context 00253 **/ 00254 00255 void dhcpServerProcessMessage(NetInterface *interface, 00256 const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, 00257 const NetBuffer *buffer, size_t offset, void *params) 00258 { 00259 size_t length; 00260 DhcpServerContext *context; 00261 DhcpMessage *message; 00262 DhcpOption *option; 00263 00264 //Point to the DHCP server context 00265 context = (DhcpServerContext *) params; 00266 00267 //Retrieve the length of the DHCP message 00268 length = netBufferGetLength(buffer) - offset; 00269 00270 //Make sure the DHCP message is valid 00271 if(length < sizeof(DhcpMessage)) 00272 return; 00273 if(length > DHCP_MAX_MSG_SIZE) 00274 return; 00275 00276 //Point to the beginning of the DHCP message 00277 message = netBufferAt(buffer, offset); 00278 //Sanity check 00279 if(message == NULL) 00280 return; 00281 00282 //Debug message 00283 TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n", 00284 formatSystemTime(osGetSystemTime(), NULL), length); 00285 00286 //Dump the contents of the message for debugging purpose 00287 dhcpDumpMessage(message, length); 00288 00289 //Check opcode 00290 if(message->op != DHCP_OPCODE_BOOTREQUEST) 00291 return; 00292 //Enforce hardware type 00293 if(message->htype != DHCP_HARDWARE_TYPE_ETH) 00294 return; 00295 //Check the length of the hardware address 00296 if(message->hlen != sizeof(MacAddr)) 00297 return; 00298 //Check magic cookie 00299 if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE)) 00300 return; 00301 00302 //Retrieve DHCP Message Type option 00303 option = dhcpGetOption(message, length, DHCP_OPT_DHCP_MESSAGE_TYPE); 00304 00305 //Failed to retrieve specified option? 00306 if(option == NULL || option->length != 1) 00307 return; 00308 00309 //Check message type 00310 switch(option->value[0]) 00311 { 00312 case DHCP_MESSAGE_TYPE_DISCOVER: 00313 //Parse DHCPDISCOVER message 00314 dhcpServerParseDiscover(context, message, length); 00315 break; 00316 case DHCP_MESSAGE_TYPE_REQUEST: 00317 //Parse DHCPREQUEST message 00318 dhcpServerParseRequest(context, message, length); 00319 break; 00320 case DHCP_MESSAGE_TYPE_DECLINE: 00321 //Parse DHCPDECLINE message 00322 dhcpServerParseDecline(context, message, length); 00323 break; 00324 case DHCP_MESSAGE_TYPE_RELEASE: 00325 //Parse DHCPRELEASE message 00326 dhcpServerParseRelease(context, message, length); 00327 break; 00328 case DHCP_MESSAGE_TYPE_INFORM: 00329 //Parse DHCPINFORM message 00330 dhcpServerParseInform(context, message, length); 00331 break; 00332 default: 00333 //Silently drop incoming message 00334 break; 00335 } 00336 } 00337 00338 00339 /** 00340 * @brief Parse DHCPDISCOVER message 00341 * @param[in] context Pointer to the DHCP server context 00342 * @param[in] message Pointer to the incoming DHCP message 00343 * @param[in] length Length of the incoming message to parse 00344 **/ 00345 00346 void dhcpServerParseDiscover(DhcpServerContext *context, 00347 const DhcpMessage *message, size_t length) 00348 { 00349 error_t error; 00350 NetInterface *interface; 00351 Ipv4Addr requestedIpAddr; 00352 DhcpOption *option; 00353 DhcpServerBinding *binding; 00354 00355 //Point to the underlying network interface 00356 interface = context->settings.interface; 00357 00358 //Retrieve Server Identifier option 00359 option = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER); 00360 00361 //Option found? 00362 if(option != NULL && option->length == 4) 00363 { 00364 //Unexpected server identifier? 00365 if(!ipv4CompAddr(option->value, &interface->ipv4Context.addr)) 00366 return; 00367 } 00368 00369 //Retrieve Requested IP Address option 00370 option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS); 00371 00372 //The client may include the 'requested IP address' option to suggest 00373 //that a particular IP address be assigned 00374 if(option != NULL && option->length == 4) 00375 ipv4CopyAddr(&requestedIpAddr, option->value); 00376 else 00377 requestedIpAddr = IPV4_UNSPECIFIED_ADDR; 00378 00379 //Search the list for a matching binding 00380 binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr); 00381 00382 //Matching binding found? 00383 if(binding != NULL) 00384 { 00385 //Different IP address than cached? 00386 if(requestedIpAddr != binding->ipAddr) 00387 { 00388 //Ensure the IP address is in the server's pool of available addresses 00389 if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) && 00390 ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax)) 00391 { 00392 //Make sure the IP address is not already allocated 00393 if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr)) 00394 { 00395 //Record IP address 00396 binding->ipAddr = requestedIpAddr; 00397 //Get current time 00398 binding->timestamp = osGetSystemTime(); 00399 } 00400 } 00401 } 00402 00403 //Sucessful processing 00404 error = NO_ERROR; 00405 } 00406 else 00407 { 00408 //Create a new binding 00409 binding = dhcpServerCreateBinding(context); 00410 00411 //Binding successfully created 00412 if(binding != NULL) 00413 { 00414 //Ensure the IP address is in the server's pool of available addresses 00415 if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) && 00416 ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax)) 00417 { 00418 //Make sure the IP address is not already allocated 00419 if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr)) 00420 { 00421 //Record IP address 00422 binding->ipAddr = requestedIpAddr; 00423 //Sucessful processing 00424 error = NO_ERROR; 00425 } 00426 else 00427 { 00428 //Retrieve the next available IP address from the pool of addresses 00429 error = dhcpServerGetNextIpAddr(context, &binding->ipAddr); 00430 } 00431 } 00432 else 00433 { 00434 //Retrieve the next available IP address from the pool of addresses 00435 error = dhcpServerGetNextIpAddr(context, &binding->ipAddr); 00436 } 00437 00438 //Check status code 00439 if(!error) 00440 { 00441 //Record MAC address 00442 binding->macAddr = message->chaddr; 00443 //Get current time 00444 binding->timestamp = osGetSystemTime(); 00445 } 00446 } 00447 else 00448 { 00449 //Failed to create a new binding 00450 error = ERROR_FAILURE; 00451 } 00452 } 00453 00454 //Check status code 00455 if(!error) 00456 { 00457 //The server responds with a DHCPOFFER message that includes an 00458 //available network address in the 'yiaddr' field (and other 00459 //configuration parameters in DHCP options) 00460 dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_OFFER, 00461 binding->ipAddr, message, length); 00462 } 00463 } 00464 00465 00466 /** 00467 * @brief Parse DHCPREQUEST message 00468 * @param[in] context Pointer to the DHCP server context 00469 * @param[in] message Pointer to the incoming DHCP message 00470 * @param[in] length Length of the incoming message to parse 00471 **/ 00472 00473 void dhcpServerParseRequest(DhcpServerContext *context, 00474 const DhcpMessage *message, size_t length) 00475 { 00476 NetInterface *interface; 00477 Ipv4Addr clientIpAddr; 00478 DhcpOption *option; 00479 DhcpServerBinding *binding; 00480 00481 //Point to the underlying network interface 00482 interface = context->settings.interface; 00483 00484 //Retrieve Server Identifier option 00485 option = dhcpGetOption(message, length, DHCP_OPT_SERVER_IDENTIFIER); 00486 00487 //Option found? 00488 if(option != NULL && option->length == 4) 00489 { 00490 //Unexpected server identifier? 00491 if(!ipv4CompAddr(option->value, &interface->ipv4Context.addr)) 00492 return; 00493 } 00494 00495 //Check the 'ciaddr' field 00496 if(message->ciaddr != IPV4_UNSPECIFIED_ADDR) 00497 { 00498 //Save client's network address 00499 clientIpAddr = message->ciaddr; 00500 } 00501 else 00502 { 00503 //Retrieve Requested IP Address option 00504 option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS); 00505 00506 //Option found? 00507 if(option != NULL && option->length == 4) 00508 ipv4CopyAddr(&clientIpAddr, option->value); 00509 else 00510 clientIpAddr = IPV4_UNSPECIFIED_ADDR; 00511 } 00512 00513 //Valid client IP address? 00514 if(clientIpAddr != IPV4_UNSPECIFIED_ADDR) 00515 { 00516 //Search the list for a matching binding 00517 binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr); 00518 00519 //Matching binding found? 00520 if(binding != NULL) 00521 { 00522 //Make sure the client's IP address is valid 00523 if(clientIpAddr == binding->ipAddr) 00524 { 00525 //Commit network address 00526 binding->validLease = TRUE; 00527 //Save lease start time 00528 binding->timestamp = osGetSystemTime(); 00529 00530 //The server responds with a DHCPACK message containing the 00531 //configuration parameters for the requesting client 00532 dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK, 00533 binding->ipAddr, message, length); 00534 00535 //Exit immediately 00536 return; 00537 } 00538 } 00539 else 00540 { 00541 //Ensure the IP address is in the server's pool of available addresses 00542 if(ntohl(clientIpAddr) >= ntohl(context->settings.ipAddrRangeMin) && 00543 ntohl(clientIpAddr) <= ntohl(context->settings.ipAddrRangeMax)) 00544 { 00545 //Make sure the IP address is not already allocated 00546 if(!dhcpServerFindBindingByIpAddr(context, clientIpAddr)) 00547 { 00548 //Create a new binding 00549 binding = dhcpServerCreateBinding(context); 00550 00551 //Binding successfully created 00552 if(binding != NULL) 00553 { 00554 //Record MAC address 00555 binding->macAddr = message->chaddr; 00556 //Record IP address 00557 binding->ipAddr = clientIpAddr; 00558 //Commit network address 00559 binding->validLease = TRUE; 00560 //Get current time 00561 binding->timestamp = osGetSystemTime(); 00562 00563 //The server responds with a DHCPACK message containing the 00564 //configuration parameters for the requesting client 00565 dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK, 00566 binding->ipAddr, message, length); 00567 00568 //Exit immediately 00569 return; 00570 } 00571 } 00572 } 00573 } 00574 } 00575 00576 //If the server is unable to satisfy the DHCPREQUEST message, the 00577 //server should respond with a DHCPNAK message 00578 dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_NAK, 00579 IPV4_UNSPECIFIED_ADDR, message, length); 00580 } 00581 00582 00583 /** 00584 * @brief Parse DHCPDECLINE message 00585 * @param[in] context Pointer to the DHCP server context 00586 * @param[in] message Pointer to the incoming DHCP message 00587 * @param[in] length Length of the incoming message to parse 00588 **/ 00589 00590 void dhcpServerParseDecline(DhcpServerContext *context, 00591 const DhcpMessage *message, size_t length) 00592 { 00593 DhcpOption *option; 00594 DhcpServerBinding *binding; 00595 Ipv4Addr requestedIpAddr; 00596 00597 //Retrieve Requested IP Address option 00598 option = dhcpGetOption(message, length, DHCP_OPT_REQUESTED_IP_ADDRESS); 00599 00600 //Option found? 00601 if(option != NULL && option->length == 4) 00602 { 00603 //Copy the requested IP address 00604 ipv4CopyAddr(&requestedIpAddr, option->value); 00605 00606 //Search the list for a matching binding 00607 binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr); 00608 00609 //Matching binding found? 00610 if(binding != NULL) 00611 { 00612 //Check the IP address against the requested IP address 00613 if(binding->ipAddr == requestedIpAddr) 00614 { 00615 //Remote the binding from the list 00616 memset(binding, 0, sizeof(DhcpServerBinding)); 00617 } 00618 } 00619 } 00620 } 00621 00622 00623 /** 00624 * @brief Parse DHCPRELEASE message 00625 * @param[in] context Pointer to the DHCP server context 00626 * @param[in] message Pointer to the incoming DHCP message 00627 * @param[in] length Length of the incoming message to parse 00628 **/ 00629 00630 void dhcpServerParseRelease(DhcpServerContext *context, 00631 const DhcpMessage *message, size_t length) 00632 { 00633 DhcpServerBinding *binding; 00634 00635 //Search the list for a matching binding 00636 binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr); 00637 00638 //Matching binding found? 00639 if(binding != NULL) 00640 { 00641 //Check the IP address against the client IP address 00642 if(binding->ipAddr == message->ciaddr) 00643 { 00644 //Release the network address and cancel remaining lease 00645 binding->validLease = FALSE; 00646 } 00647 } 00648 } 00649 00650 00651 /** 00652 * @brief Parse DHCPINFORM message 00653 * @param[in] context Pointer to the DHCP server context 00654 * @param[in] message Pointer to the incoming DHCP message 00655 * @param[in] length Length of the incoming message to parse 00656 **/ 00657 00658 void dhcpServerParseInform(DhcpServerContext *context, 00659 const DhcpMessage *message, size_t length) 00660 { 00661 //Make sure the client IP address is valid 00662 if(message->ciaddr != IPV4_UNSPECIFIED_ADDR) 00663 { 00664 //Servers receiving a DHCPINFORM message construct a DHCPACK message 00665 //with any local configuration parameters appropriate for the client 00666 dhcpServerSendReply(context, DHCP_MESSAGE_TYPE_ACK, 00667 IPV4_UNSPECIFIED_ADDR, message, length); 00668 } 00669 } 00670 00671 00672 /** 00673 * @brief Send DHCP reply message 00674 * @param[in] context Pointer to the DHCP server context 00675 * @param[in] type DHCP message type (DHCPOFFER, DHCPACK or DHCPNAK) 00676 * @param[in] yourIpAddr The IP address to be placed in the 'yiaddr' field 00677 * @param[in] request Pointer to DHCP message received from the client 00678 * @param[in] length Length of the DHCP message received from the client 00679 * @return Error code 00680 **/ 00681 00682 error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type, 00683 Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length) 00684 { 00685 error_t error; 00686 uint_t n; 00687 uint32_t value; 00688 size_t offset; 00689 NetBuffer *buffer; 00690 NetInterface *interface; 00691 DhcpMessage *reply; 00692 IpAddr destIpAddr; 00693 uint16_t destPort; 00694 00695 //Point to the underlying network interface 00696 interface = context->settings.interface; 00697 00698 //Allocate a memory buffer to hold the DHCP message 00699 buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset); 00700 //Failed to allocate buffer? 00701 if(buffer == NULL) 00702 return ERROR_OUT_OF_MEMORY; 00703 00704 //Point to the beginning of the DHCP message 00705 reply = netBufferAt(buffer, offset); 00706 //Clear memory buffer contents 00707 memset(reply, 0, DHCP_MIN_MSG_SIZE); 00708 00709 //Format DHCP reply message 00710 reply->op = DHCP_OPCODE_BOOTREPLY; 00711 reply->htype = DHCP_HARDWARE_TYPE_ETH; 00712 reply->hlen = sizeof(MacAddr); 00713 reply->xid = request->xid; 00714 reply->secs = 0; 00715 reply->flags = request->flags; 00716 reply->ciaddr = IPV4_UNSPECIFIED_ADDR; 00717 reply->yiaddr = yourIpAddr; 00718 reply->siaddr = IPV4_UNSPECIFIED_ADDR; 00719 reply->giaddr = request->giaddr; 00720 reply->chaddr = request->chaddr; 00721 00722 //Write magic cookie before setting any option 00723 reply->magicCookie = HTONL(DHCP_MAGIC_COOKIE); 00724 //Properly terminate options field 00725 reply->options[0] = DHCP_OPT_END; 00726 00727 //Add DHCP Message Type option 00728 dhcpAddOption(reply, DHCP_OPT_DHCP_MESSAGE_TYPE, 00729 &type, sizeof(type)); 00730 00731 //Add Server Identifier option 00732 dhcpAddOption(reply, DHCP_OPT_SERVER_IDENTIFIER, 00733 &interface->ipv4Context.addr, sizeof(Ipv4Addr)); 00734 00735 //DHCPOFFER or DHCPACK message? 00736 if(type == DHCP_MESSAGE_TYPE_OFFER || type == DHCP_MESSAGE_TYPE_ACK) 00737 { 00738 //Convert the lease time to network byte order 00739 value = htonl(context->settings.leaseTime); 00740 00741 //When responding to a DHCPINFORM message, the server must not 00742 //send a lease expiration time to the client 00743 if(yourIpAddr != IPV4_UNSPECIFIED_ADDR) 00744 { 00745 //Add IP Address Lease Time option 00746 dhcpAddOption(reply, DHCP_OPT_IP_ADDRESS_LEASE_TIME, 00747 &value, sizeof(value)); 00748 } 00749 00750 //Add Subnet Mask option 00751 if(context->settings.subnetMask != IPV4_UNSPECIFIED_ADDR) 00752 { 00753 dhcpAddOption(reply, DHCP_OPT_SUBNET_MASK, 00754 &context->settings.subnetMask, sizeof(Ipv4Addr)); 00755 } 00756 00757 //Add Router option 00758 if(context->settings.defaultGateway != IPV4_UNSPECIFIED_ADDR) 00759 { 00760 dhcpAddOption(reply, DHCP_OPT_ROUTER, 00761 &context->settings.defaultGateway, sizeof(Ipv4Addr)); 00762 } 00763 00764 //Retrieve the number of DNS servers 00765 for(n = 0; n < DHCP_SERVER_MAX_DNS_SERVERS; n++) 00766 { 00767 //Check whether the current DNS server is valid 00768 if(context->settings.dnsServer[n] == IPV4_UNSPECIFIED_ADDR) 00769 break; 00770 } 00771 00772 //Add DNS Server option 00773 if(n > 0) 00774 { 00775 dhcpAddOption(reply, DHCP_OPT_DNS_SERVER, 00776 context->settings.dnsServer, n * sizeof(Ipv4Addr)); 00777 } 00778 } 00779 00780 //Check whether the 'giaddr' field is non-zero 00781 if(request->giaddr != IPV4_UNSPECIFIED_ADDR) 00782 { 00783 //If the 'giaddr' field in a DHCP message from a client is non-zero, 00784 //the server sends any return messages to the 'DHCP server' port 00785 destPort = DHCP_SERVER_PORT; 00786 00787 //The DHCP message is sent to the BOOTP relay agent whose address 00788 //appears in 'giaddr' 00789 destIpAddr.length = sizeof(Ipv4Addr); 00790 destIpAddr.ipv4Addr = request->giaddr; 00791 } 00792 else 00793 { 00794 //If the 'giaddr' field in a DHCP message from a client is zero, 00795 //the server sends any return messages to the 'DHCP client' 00796 destPort = DHCP_CLIENT_PORT; 00797 00798 //DHCPOFFER or DHCPACK message? 00799 if(type == DHCP_MESSAGE_TYPE_OFFER || type == DHCP_MESSAGE_TYPE_ACK) 00800 { 00801 //Check whether the 'giaddr' field is non-zero 00802 if(request->ciaddr != IPV4_UNSPECIFIED_ADDR) 00803 { 00804 //If the 'giaddr' field is zero and the 'ciaddr' field is nonzero, 00805 //then the server unicasts DHCPOFFER and DHCPACK messages to the 00806 //address in 'ciaddr' 00807 destIpAddr.length = sizeof(Ipv4Addr); 00808 destIpAddr.ipv4Addr = request->ciaddr; 00809 } 00810 else 00811 { 00812 //Check whether the broadcast bit is set 00813 if(ntohs(request->flags) & DHCP_FLAG_BROADCAST) 00814 { 00815 //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is 00816 //set, then the server broadcasts DHCPOFFER and DHCPACK messages 00817 destIpAddr.length = sizeof(Ipv4Addr); 00818 destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR; 00819 } 00820 else 00821 { 00822 //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is 00823 //not set, then the server unicasts DHCPOFFER and DHCPACK messages 00824 //to the client's hardware address and 'yiaddr' address 00825 destIpAddr.length = sizeof(Ipv4Addr); 00826 destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR; 00827 } 00828 } 00829 } 00830 //DHCPNAK message? 00831 else 00832 { 00833 //In all cases, when 'giaddr' is zero, the server broadcasts any 00834 //DHCPNAK messages 00835 destIpAddr.length = sizeof(Ipv4Addr); 00836 destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR; 00837 } 00838 } 00839 00840 //Debug message 00841 TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n", 00842 formatSystemTime(osGetSystemTime(), NULL), DHCP_MIN_MSG_SIZE); 00843 00844 //Dump the contents of the message for debugging purpose 00845 dhcpDumpMessage(reply, DHCP_MIN_MSG_SIZE); 00846 00847 //Broadcast DHCPDECLINE message 00848 error = udpSendDatagramEx(interface, DHCP_SERVER_PORT, &destIpAddr, 00849 destPort, buffer, offset, IPV4_DEFAULT_TTL); 00850 00851 //Free previously allocated memory 00852 netBufferFree(buffer); 00853 //Return status code 00854 return error; 00855 } 00856 00857 00858 /** 00859 * @brief Create a new binding 00860 * @param[in] context Pointer to the DHCP server context 00861 * @return Pointer to the newly created binding 00862 **/ 00863 00864 DhcpServerBinding *dhcpServerCreateBinding(DhcpServerContext *context) 00865 { 00866 uint_t i; 00867 DhcpServerBinding *binding; 00868 DhcpServerBinding *oldestBinding; 00869 00870 //Keep track of the oldest binding 00871 oldestBinding = NULL; 00872 00873 //Loop through the list of bindings 00874 for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++) 00875 { 00876 //Point to the current binding 00877 binding = &context->clientBinding[i]; 00878 00879 //Check whether the binding is available 00880 if(macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR)) 00881 { 00882 //Erase contents 00883 memset(binding, 0, sizeof(DhcpServerBinding)); 00884 //Return a pointer to the newly created binding 00885 return binding; 00886 } 00887 else 00888 { 00889 //Bindings that have been committed cannot be removed 00890 if(!binding->validLease) 00891 { 00892 //Keep track of the oldest binding in the list 00893 if(oldestBinding != NULL) 00894 { 00895 if(timeCompare(binding->timestamp, oldestBinding->timestamp) < 0) 00896 oldestBinding = binding; 00897 } 00898 else 00899 { 00900 oldestBinding = binding; 00901 } 00902 } 00903 } 00904 } 00905 00906 //Any binding available in the list? 00907 if(oldestBinding != NULL) 00908 { 00909 //Erase contents 00910 memset(oldestBinding, 0, sizeof(DhcpServerBinding)); 00911 } 00912 00913 //Return a pointer to the oldest binding 00914 return oldestBinding; 00915 } 00916 00917 00918 /** 00919 * @brief Search the list of bindings for a given MAC address 00920 * @param[in] context Pointer to the DHCP server context 00921 * @param[in] macAddr MAC address 00922 * @return Pointer to the corresponding DHCP binding 00923 **/ 00924 00925 DhcpServerBinding *dhcpServerFindBindingByMacAddr(DhcpServerContext *context, 00926 const MacAddr* macAddr) 00927 { 00928 uint_t i; 00929 DhcpServerBinding *binding; 00930 00931 //Loop through the list of bindings 00932 for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++) 00933 { 00934 //Point to the current binding 00935 binding = &context->clientBinding[i]; 00936 00937 //Valid binding? 00938 if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR)) 00939 { 00940 //Check whether the current binding matches the specified MAC address 00941 if(macCompAddr(&binding->macAddr, macAddr)) 00942 { 00943 //Return the pointer to the corresponding binding 00944 return binding; 00945 } 00946 } 00947 } 00948 00949 //No matching binding... 00950 return NULL; 00951 } 00952 00953 00954 /** 00955 * @brief Search the list of bindings for a given IP address 00956 * @param[in] context Pointer to the DHCP server context 00957 * @param[in] ipAddr IP address 00958 * @return Pointer to the corresponding DHCP binding 00959 **/ 00960 00961 DhcpServerBinding *dhcpServerFindBindingByIpAddr(DhcpServerContext *context, 00962 Ipv4Addr ipAddr) 00963 { 00964 uint_t i; 00965 DhcpServerBinding *binding; 00966 00967 //Loop through the list of bindings 00968 for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++) 00969 { 00970 //Point to the current binding 00971 binding = &context->clientBinding[i]; 00972 00973 //Valid binding? 00974 if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR)) 00975 { 00976 //Check whether the current binding matches the specified IP address 00977 if(binding->ipAddr == ipAddr) 00978 { 00979 //Return the pointer to the corresponding binding 00980 return binding; 00981 } 00982 } 00983 } 00984 00985 //No matching binding... 00986 return NULL; 00987 } 00988 00989 00990 /** 00991 * @brief Retrieve the next IP address to be used 00992 * @param[in] context Pointer to the DHCP server context 00993 * @param[out] ipAddr Next IP address to be used 00994 * @return Error code 00995 **/ 00996 00997 error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr) 00998 { 00999 uint_t i; 01000 DhcpServerBinding *binding; 01001 01002 //Search the pool for any available IP address 01003 for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++) 01004 { 01005 //Check whether the current IP address is already allocated 01006 binding = dhcpServerFindBindingByIpAddr(context, context->nextIpAddr); 01007 01008 //If the IP address is available, then it can be assigned to a new client 01009 if(binding == NULL) 01010 *ipAddr = context->nextIpAddr; 01011 01012 //Compute the next IP address that will be assigned by the DHCP server 01013 if(ntohl(context->nextIpAddr) >= ntohl(context->settings.ipAddrRangeMax)) 01014 { 01015 //Wrap around to the beginning of the pool 01016 context->nextIpAddr = context->settings.ipAddrRangeMin; 01017 } 01018 else 01019 { 01020 //Increment IP address 01021 context->nextIpAddr = htonl(ntohl(context->nextIpAddr) + 1); 01022 } 01023 01024 //If the IP address is available, we are done 01025 if(binding == NULL) 01026 return NO_ERROR; 01027 } 01028 01029 //No available addresses in the pool... 01030 return ERROR_NO_ADDRESS; 01031 } 01032 01033 #endif 01034
Generated on Tue Jul 12 2022 17:10:13 by
