Webserver+3d print

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers dhcp_server.c Source File

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