dhcp_server.c
Go to the documentation of this file.
1 /**
2  * @file dhcp_server.c
3  * @brief DHCP server (Dynamic Host Configuration Protocol)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * The Dynamic Host Configuration Protocol is used to provide configuration
30  * parameters to hosts. Refer to the following RFCs for complete details:
31  * - RFC 2131: Dynamic Host Configuration Protocol
32  * - RFC 2132: DHCP Options and BOOTP Vendor Extensions
33  * - RFC 4039: Rapid Commit Option for the DHCP version 4
34  *
35  * @author Oryx Embedded SARL (www.oryx-embedded.com)
36  * @version 1.9.6
37  **/
38 
39 //Switch to the appropriate trace level
40 #define TRACE_LEVEL DHCP_TRACE_LEVEL
41 
42 //Dependencies
43 #include "core/net.h"
44 #include "dhcp/dhcp_server.h"
45 #include "dhcp/dhcp_common.h"
46 #include "dhcp/dhcp_debug.h"
47 #include "date_time.h"
48 #include "debug.h"
49 
50 //Check TCP/IP stack configuration
51 #if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
52 
53 //Tick counter to handle periodic operations
55 
56 
57 /**
58  * @brief Initialize settings with default values
59  * @param[out] settings Structure that contains DHCP server settings
60  **/
61 
63 {
64  uint_t i;
65 
66  //Use default interface
67  settings->interface = netGetDefaultInterface();
68  //Index of the IP address assigned to the DHCP server
69  settings->ipAddrIndex = 0;
70 
71  //Support for quick configuration using rapid commit
72  settings->rapidCommit = FALSE;
73  //Lease time, in seconds, assigned to the DHCP clients
75 
76  //Lowest and highest IP addresses in the pool that are available
77  //for dynamic address assignment
80 
81  //Subnet mask
83  //Default gateway
85 
86  //DNS servers
87  for(i = 0; i < DHCP_SERVER_MAX_DNS_SERVERS; i++)
88  settings->dnsServer[i] = IPV4_UNSPECIFIED_ADDR;
89 }
90 
91 
92 /**
93  * @brief DHCP server initialization
94  * @param[in] context Pointer to the DHCP server context
95  * @param[in] settings DHCP server specific settings
96  * @return Error code
97  **/
98 
100  const DhcpServerSettings *settings)
101 {
102  error_t error;
103  NetInterface *interface;
104 
105  //Debug message
106  TRACE_INFO("Initializing DHCP server...\r\n");
107 
108  //Ensure the parameters are valid
109  if(context == NULL || settings == NULL)
111 
112  //Valid network interface?
113  if(settings->interface == NULL)
115 
116  //Get exclusive access
118 
119  //Point to the underlying network interface
120  interface = settings->interface;
121 
122  //Clear the DHCP server context
123  memset(context, 0, sizeof(DhcpServerContext));
124  //Save user settings
125  context->settings = *settings;
126 
127  //Next IP address that will be assigned by the DHCP server
128  context->nextIpAddr = settings->ipAddrRangeMin;
129  //DHCP server is currently suspended
130  context->running = FALSE;
131 
132  //Callback function to be called when a DHCP message is received
133  error = udpAttachRxCallback(interface, DHCP_SERVER_PORT,
134  dhcpServerProcessMessage, context);
135 
136  //Check status code
137  if(!error)
138  {
139  //Attach the DHCP server context to the network interface
140  interface->dhcpServerContext = context;
141  }
142 
143  //Release exclusive access
145 
146  //Return status code
147  return error;
148 }
149 
150 
151 /**
152  * @brief Release DHCP server context
153  * @param[in] context Pointer to the DHCP server context
154  **/
155 
157 {
158  NetInterface *interface;
159 
160  //Make sure the DHCP server context is valid
161  if(context != NULL)
162  {
163  //Get exclusive access
165 
166  //Point to the underlying network interface
167  interface = context->settings.interface;
168 
169  //Valid network interface?
170  if(interface != NULL)
171  {
172  //Detach the DHCP server context from the network interface
173  interface->dhcpServerContext = NULL;
174 
175  //Unregister callback function
177  }
178 
179  //Clear the DHCP server context
180  memset(context, 0, sizeof(DhcpServerContext));
181 
182  //Release exclusive access
184  }
185 }
186 
187 
188 /**
189  * @brief Start DHCP server
190  * @param[in] context Pointer to the DHCP server context
191  * @return Error code
192  **/
193 
195 {
196  //Check parameter
197  if(context == NULL)
199 
200  //Debug message
201  TRACE_INFO("Starting DHCP server...\r\n");
202 
203  //Get exclusive access
205  //Start DHCP server
206  context->running = TRUE;
207  //Release exclusive access
209 
210  //Successful processing
211  return NO_ERROR;
212 }
213 
214 
215 /**
216  * @brief Stop DHCP server
217  * @param[in] context Pointer to the DHCP server context
218  * @return Error code
219  **/
220 
222 {
223  //Check parameter
224  if(context == NULL)
226 
227  //Debug message
228  TRACE_INFO("Stopping DHCP server...\r\n");
229 
230  //Get exclusive access
232  //Stop DHCP server
233  context->running = FALSE;
234  //Release exclusive access
236 
237  //Successful processing
238  return NO_ERROR;
239 }
240 
241 
242 /**
243  * @brief DHCP server timer handler
244  *
245  * This routine must be periodically called by the TCP/IP stack to
246  * manage DHCP server operation
247  *
248  * @param[in] context Pointer to the DHCP server context
249  **/
250 
252 {
253  uint_t i;
254  systime_t time;
255  systime_t leaseTime;
256  DhcpServerBinding *binding;
257 
258  //Make sure the DHCP server has been properly instantiated
259  if(context == NULL)
260  return;
261 
262  //Get current time
263  time = osGetSystemTime();
264 
265  //Convert the lease time to milliseconds
266  if(context->settings.leaseTime < (MAX_DELAY / 1000))
267  leaseTime = context->settings.leaseTime * 1000;
268  else
269  leaseTime = MAX_DELAY;
270 
271  //Loop through the list of bindings
272  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
273  {
274  //Point to the current binding
275  binding = &context->clientBinding[i];
276 
277  //Valid binding?
278  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
279  {
280  //Check whether the network address has been committed
281  if(binding->validLease)
282  {
283  //Check whether the lease has expired
284  if(timeCompare(time, binding->timestamp + leaseTime) >= 0)
285  {
286  //The address lease is not more valid
287  binding->validLease = FALSE;
288  }
289  }
290  }
291  }
292 }
293 
294 
295 /**
296  * @brief Process incoming DHCP message
297  * @param[in] interface Underlying network interface
298  * @param[in] pseudoHeader UDP pseudo header
299  * @param[in] udpHeader UDP header
300  * @param[in] buffer Multi-part buffer containing the incoming DHCP message
301  * @param[in] offset Offset to the first byte of the DHCP message
302  * @param[in] param Pointer to the DHCP server context
303  **/
304 
306  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
307  const NetBuffer *buffer, size_t offset, void *param)
308 {
309  size_t length;
310  DhcpServerContext *context;
312  DhcpOption *option;
313 
314  //Point to the DHCP server context
315  context = (DhcpServerContext *) param;
316 
317  //Retrieve the length of the DHCP message
318  length = netBufferGetLength(buffer) - offset;
319 
320  //Make sure the DHCP message is valid
321  if(length < sizeof(DhcpMessage))
322  return;
324  return;
325 
326  //Point to the beginning of the DHCP message
327  message = netBufferAt(buffer, offset);
328  //Sanity check
329  if(message == NULL)
330  return;
331 
332  //Debug message
333  TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
335 
336  //Dump the contents of the message for debugging purpose
338 
339  //Check opcode
341  return;
342  //Enforce hardware type
343  if(message->htype != DHCP_HARDWARE_TYPE_ETH)
344  return;
345  //Check the length of the hardware address
346  if(message->hlen != sizeof(MacAddr))
347  return;
348  //Check magic cookie
349  if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
350  return;
351 
352  //Retrieve DHCP Message Type option
354 
355  //Failed to retrieve specified option?
356  if(option == NULL || option->length != 1)
357  return;
358 
359  //Check message type
360  switch(option->value[0])
361  {
363  //Parse DHCPDISCOVER message
365  break;
367  //Parse DHCPREQUEST message
369  break;
371  //Parse DHCPDECLINE message
373  break;
375  //Parse DHCPRELEASE message
377  break;
379  //Parse DHCPINFORM message
381  break;
382  default:
383  //Silently drop incoming message
384  break;
385  }
386 }
387 
388 
389 /**
390  * @brief Parse DHCPDISCOVER message
391  * @param[in] context Pointer to the DHCP server context
392  * @param[in] message Pointer to the incoming DHCP message
393  * @param[in] length Length of the incoming message to parse
394  **/
395 
397  const DhcpMessage *message, size_t length)
398 {
399  error_t error;
400  uint_t i;
401  NetInterface *interface;
402  Ipv4Addr requestedIpAddr;
403  DhcpOption *option;
404  DhcpServerBinding *binding;
405 
406  //Point to the underlying network interface
407  interface = context->settings.interface;
408  //Index of the IP address assigned to the DHCP server
409  i = context->settings.ipAddrIndex;
410 
411  //Retrieve Server Identifier option
413 
414  //Option found?
415  if(option != NULL && option->length == 4)
416  {
417  //Unexpected server identifier?
418  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
419  return;
420  }
421 
422  //Retrieve Requested IP Address option
424 
425  //The client may include the 'requested IP address' option to suggest
426  //that a particular IP address be assigned
427  if(option != NULL && option->length == 4)
428  ipv4CopyAddr(&requestedIpAddr, option->value);
429  else
430  requestedIpAddr = IPV4_UNSPECIFIED_ADDR;
431 
432  //Search the list for a matching binding
433  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
434 
435  //Matching binding found?
436  if(binding != NULL)
437  {
438  //Different IP address than cached?
439  if(requestedIpAddr != binding->ipAddr)
440  {
441  //Ensure the IP address is in the server's pool of available addresses
442  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
443  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
444  {
445  //Make sure the IP address is not already allocated
446  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
447  {
448  //Record IP address
449  binding->ipAddr = requestedIpAddr;
450  //Get current time
451  binding->timestamp = osGetSystemTime();
452  }
453  }
454  }
455 
456  //Sucessful processing
457  error = NO_ERROR;
458  }
459  else
460  {
461  //Create a new binding
462  binding = dhcpServerCreateBinding(context);
463 
464  //Binding successfully created
465  if(binding != NULL)
466  {
467  //Ensure the IP address is in the server's pool of available addresses
468  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
469  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
470  {
471  //Make sure the IP address is not already allocated
472  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
473  {
474  //Record IP address
475  binding->ipAddr = requestedIpAddr;
476  //Sucessful processing
477  error = NO_ERROR;
478  }
479  else
480  {
481  //Retrieve the next available IP address from the pool of addresses
482  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
483  }
484  }
485  else
486  {
487  //Retrieve the next available IP address from the pool of addresses
488  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
489  }
490 
491  //Check status code
492  if(!error)
493  {
494  //Record MAC address
495  binding->macAddr = message->chaddr;
496  //Get current time
497  binding->timestamp = osGetSystemTime();
498  }
499  }
500  else
501  {
502  //Failed to create a new binding
503  error = ERROR_FAILURE;
504  }
505  }
506 
507  //Check status code
508  if(!error)
509  {
510  //The server responds with a DHCPOFFER message that includes an
511  //available network address in the 'yiaddr' field (and other
512  //configuration parameters in DHCP options)
514  binding->ipAddr, message, length);
515  }
516 }
517 
518 
519 /**
520  * @brief Parse DHCPREQUEST message
521  * @param[in] context Pointer to the DHCP server context
522  * @param[in] message Pointer to the incoming DHCP message
523  * @param[in] length Length of the incoming message to parse
524  **/
525 
527  const DhcpMessage *message, size_t length)
528 {
529  uint_t i;
530  NetInterface *interface;
531  Ipv4Addr clientIpAddr;
532  DhcpOption *option;
533  DhcpServerBinding *binding;
534 
535  //Point to the underlying network interface
536  interface = context->settings.interface;
537  //Index of the IP address assigned to the DHCP server
538  i = context->settings.ipAddrIndex;
539 
540  //Retrieve Server Identifier option
542 
543  //Option found?
544  if(option != NULL && option->length == 4)
545  {
546  //Unexpected server identifier?
547  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
548  return;
549  }
550 
551  //Check the 'ciaddr' field
552  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
553  {
554  //Save client's network address
555  clientIpAddr = message->ciaddr;
556  }
557  else
558  {
559  //Retrieve Requested IP Address option
561 
562  //Option found?
563  if(option != NULL && option->length == 4)
564  ipv4CopyAddr(&clientIpAddr, option->value);
565  else
566  clientIpAddr = IPV4_UNSPECIFIED_ADDR;
567  }
568 
569  //Valid client IP address?
570  if(clientIpAddr != IPV4_UNSPECIFIED_ADDR)
571  {
572  //Search the list for a matching binding
573  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
574 
575  //Matching binding found?
576  if(binding != NULL)
577  {
578  //Make sure the client's IP address is valid
579  if(clientIpAddr == binding->ipAddr)
580  {
581  //Commit network address
582  binding->validLease = TRUE;
583  //Save lease start time
584  binding->timestamp = osGetSystemTime();
585 
586  //The server responds with a DHCPACK message containing the
587  //configuration parameters for the requesting client
589  binding->ipAddr, message, length);
590 
591  //Exit immediately
592  return;
593  }
594  }
595  else
596  {
597  //Ensure the IP address is in the server's pool of available addresses
598  if(ntohl(clientIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
599  ntohl(clientIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
600  {
601  //Make sure the IP address is not already allocated
602  if(!dhcpServerFindBindingByIpAddr(context, clientIpAddr))
603  {
604  //Create a new binding
605  binding = dhcpServerCreateBinding(context);
606 
607  //Binding successfully created
608  if(binding != NULL)
609  {
610  //Record MAC address
611  binding->macAddr = message->chaddr;
612  //Record IP address
613  binding->ipAddr = clientIpAddr;
614  //Commit network address
615  binding->validLease = TRUE;
616  //Get current time
617  binding->timestamp = osGetSystemTime();
618 
619  //The server responds with a DHCPACK message containing the
620  //configuration parameters for the requesting client
622  binding->ipAddr, message, length);
623 
624  //Exit immediately
625  return;
626  }
627  }
628  }
629  }
630  }
631 
632  //If the server is unable to satisfy the DHCPREQUEST message, the
633  //server should respond with a DHCPNAK message
636 }
637 
638 
639 /**
640  * @brief Parse DHCPDECLINE message
641  * @param[in] context Pointer to the DHCP server context
642  * @param[in] message Pointer to the incoming DHCP message
643  * @param[in] length Length of the incoming message to parse
644  **/
645 
647  const DhcpMessage *message, size_t length)
648 {
649  DhcpOption *option;
650  DhcpServerBinding *binding;
651  Ipv4Addr requestedIpAddr;
652 
653  //Retrieve Requested IP Address option
655 
656  //Option found?
657  if(option != NULL && option->length == 4)
658  {
659  //Copy the requested IP address
660  ipv4CopyAddr(&requestedIpAddr, option->value);
661 
662  //Search the list for a matching binding
663  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
664 
665  //Matching binding found?
666  if(binding != NULL)
667  {
668  //Check the IP address against the requested IP address
669  if(binding->ipAddr == requestedIpAddr)
670  {
671  //Remote the binding from the list
672  memset(binding, 0, sizeof(DhcpServerBinding));
673  }
674  }
675  }
676 }
677 
678 
679 /**
680  * @brief Parse DHCPRELEASE message
681  * @param[in] context Pointer to the DHCP server context
682  * @param[in] message Pointer to the incoming DHCP message
683  * @param[in] length Length of the incoming message to parse
684  **/
685 
687  const DhcpMessage *message, size_t length)
688 {
689  DhcpServerBinding *binding;
690 
691  //Search the list for a matching binding
692  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
693 
694  //Matching binding found?
695  if(binding != NULL)
696  {
697  //Check the IP address against the client IP address
698  if(binding->ipAddr == message->ciaddr)
699  {
700  //Release the network address and cancel remaining lease
701  binding->validLease = FALSE;
702  }
703  }
704 }
705 
706 
707 /**
708  * @brief Parse DHCPINFORM message
709  * @param[in] context Pointer to the DHCP server context
710  * @param[in] message Pointer to the incoming DHCP message
711  * @param[in] length Length of the incoming message to parse
712  **/
713 
715  const DhcpMessage *message, size_t length)
716 {
717  //Make sure the client IP address is valid
718  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
719  {
720  //Servers receiving a DHCPINFORM message construct a DHCPACK message
721  //with any local configuration parameters appropriate for the client
724  }
725 }
726 
727 
728 /**
729  * @brief Send DHCP reply message
730  * @param[in] context Pointer to the DHCP server context
731  * @param[in] type DHCP message type (DHCPOFFER, DHCPACK or DHCPNAK)
732  * @param[in] yourIpAddr The IP address to be placed in the 'yiaddr' field
733  * @param[in] request Pointer to DHCP message received from the client
734  * @param[in] length Length of the DHCP message received from the client
735  * @return Error code
736  **/
737 
739  Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length)
740 {
741  error_t error;
742  uint_t i;
743  uint_t n;
744  uint32_t value;
745  size_t offset;
746  NetBuffer *buffer;
747  NetInterface *interface;
748  DhcpMessage *reply;
751  uint16_t destPort;
752 
753  //Point to the underlying network interface
754  interface = context->settings.interface;
755  //Index of the IP address assigned to the DHCP server
756  i = context->settings.ipAddrIndex;
757 
758  //Allocate a memory buffer to hold the DHCP message
759  buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
760  //Failed to allocate buffer?
761  if(buffer == NULL)
762  return ERROR_OUT_OF_MEMORY;
763 
764  //Point to the beginning of the DHCP message
765  reply = netBufferAt(buffer, offset);
766  //Clear memory buffer contents
767  memset(reply, 0, DHCP_MIN_MSG_SIZE);
768 
769  //Format DHCP reply message
770  reply->op = DHCP_OPCODE_BOOTREPLY;
771  reply->htype = DHCP_HARDWARE_TYPE_ETH;
772  reply->hlen = sizeof(MacAddr);
773  reply->xid = request->xid;
774  reply->secs = 0;
775  reply->flags = request->flags;
776  reply->ciaddr = IPV4_UNSPECIFIED_ADDR;
777  reply->yiaddr = yourIpAddr;
778  reply->siaddr = IPV4_UNSPECIFIED_ADDR;
779  reply->giaddr = request->giaddr;
780  reply->chaddr = request->chaddr;
781 
782  //Write magic cookie before setting any option
783  reply->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
784  //Properly terminate options field
785  reply->options[0] = DHCP_OPT_END;
786 
787  //Add DHCP Message Type option
789  &type, sizeof(type));
790 
791  //Add Server Identifier option
793  &interface->ipv4Context.addrList[i].addr, sizeof(Ipv4Addr));
794 
795  //DHCPOFFER or DHCPACK message?
797  {
798  //Convert the lease time to network byte order
799  value = htonl(context->settings.leaseTime);
800 
801  //When responding to a DHCPINFORM message, the server must not
802  //send a lease expiration time to the client
803  if(yourIpAddr != IPV4_UNSPECIFIED_ADDR)
804  {
805  //Add IP Address Lease Time option
807  &value, sizeof(value));
808  }
809 
810  //Add Subnet Mask option
812  {
814  &context->settings.subnetMask, sizeof(Ipv4Addr));
815  }
816 
817  //Add Router option
819  {
821  &context->settings.defaultGateway, sizeof(Ipv4Addr));
822  }
823 
824  //Retrieve the number of DNS servers
825  for(n = 0; n < DHCP_SERVER_MAX_DNS_SERVERS; n++)
826  {
827  //Check whether the current DNS server is valid
828  if(context->settings.dnsServer[n] == IPV4_UNSPECIFIED_ADDR)
829  break;
830  }
831 
832  //Add DNS Server option
833  if(n > 0)
834  {
836  context->settings.dnsServer, n * sizeof(Ipv4Addr));
837  }
838  }
839 
840  //A server with multiple network address (e.g. a multi-homed host) may
841  //use any of its network addresses in outgoing DHCP messages (refer to
842  //RFC 2131, section 4.1)
843  srcIpAddr.length = sizeof(Ipv4Addr);
844  srcIpAddr.ipv4Addr = interface->ipv4Context.addrList[i].addr;
845 
846  //Check whether the 'giaddr' field is non-zero
847  if(request->giaddr != IPV4_UNSPECIFIED_ADDR)
848  {
849  //If the 'giaddr' field in a DHCP message from a client is non-zero,
850  //the server sends any return messages to the 'DHCP server' port
852 
853  //The DHCP message is sent to the BOOTP relay agent whose address
854  //appears in 'giaddr'
855  destIpAddr.length = sizeof(Ipv4Addr);
856  destIpAddr.ipv4Addr = request->giaddr;
857  }
858  else
859  {
860  //If the 'giaddr' field in a DHCP message from a client is zero,
861  //the server sends any return messages to the 'DHCP client'
863 
864  //DHCPOFFER or DHCPACK message?
866  {
867  //Check whether the 'giaddr' field is non-zero
868  if(request->ciaddr != IPV4_UNSPECIFIED_ADDR)
869  {
870  //If the 'giaddr' field is zero and the 'ciaddr' field is nonzero,
871  //then the server unicasts DHCPOFFER and DHCPACK messages to the
872  //address in 'ciaddr'
873  destIpAddr.length = sizeof(Ipv4Addr);
874  destIpAddr.ipv4Addr = request->ciaddr;
875  }
876  else
877  {
878  //Check whether the broadcast bit is set
879  if(ntohs(request->flags) & DHCP_FLAG_BROADCAST)
880  {
881  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
882  //set, then the server broadcasts DHCPOFFER and DHCPACK messages
883  destIpAddr.length = sizeof(Ipv4Addr);
884  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
885  }
886  else
887  {
888  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
889  //not set, then the server unicasts DHCPOFFER and DHCPACK messages
890  //to the client's hardware address and 'yiaddr' address
891  destIpAddr.length = sizeof(Ipv4Addr);
892  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
893  }
894  }
895  }
896  //DHCPNAK message?
897  else
898  {
899  //In all cases, when 'giaddr' is zero, the server broadcasts any
900  //DHCPNAK messages
901  destIpAddr.length = sizeof(Ipv4Addr);
902  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
903  }
904  }
905 
906  //Debug message
907  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
909 
910  //Dump the contents of the message for debugging purpose
912 
913  //Send DHCP reply
914  error = udpSendDatagramEx(interface, &srcIpAddr, DHCP_SERVER_PORT,
915  &destIpAddr, destPort, buffer, offset, IPV4_DEFAULT_TTL);
916 
917  //Free previously allocated memory
918  netBufferFree(buffer);
919  //Return status code
920  return error;
921 }
922 
923 
924 /**
925  * @brief Create a new binding
926  * @param[in] context Pointer to the DHCP server context
927  * @return Pointer to the newly created binding
928  **/
929 
931 {
932  uint_t i;
933  systime_t time;
934  DhcpServerBinding *binding;
935  DhcpServerBinding *oldestBinding;
936 
937  //Get current time
938  time = osGetSystemTime();
939 
940  //Keep track of the oldest binding
941  oldestBinding = NULL;
942 
943  //Loop through the list of bindings
944  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
945  {
946  //Point to the current binding
947  binding = &context->clientBinding[i];
948 
949  //Check whether the binding is available
950  if(macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
951  {
952  //Erase contents
953  memset(binding, 0, sizeof(DhcpServerBinding));
954  //Return a pointer to the newly created binding
955  return binding;
956  }
957  else
958  {
959  //Bindings that have been committed cannot be removed
960  if(!binding->validLease)
961  {
962  //Keep track of the oldest binding in the list
963  if(oldestBinding == NULL)
964  {
965  oldestBinding = binding;
966  }
967  else if((time - binding->timestamp) > (time - oldestBinding->timestamp))
968  {
969  oldestBinding = binding;
970  }
971  }
972  }
973  }
974 
975  //Any binding available in the list?
976  if(oldestBinding != NULL)
977  {
978  //Erase contents
979  memset(oldestBinding, 0, sizeof(DhcpServerBinding));
980  }
981 
982  //Return a pointer to the oldest binding
983  return oldestBinding;
984 }
985 
986 
987 /**
988  * @brief Search the list of bindings for a given MAC address
989  * @param[in] context Pointer to the DHCP server context
990  * @param[in] macAddr MAC address
991  * @return Pointer to the corresponding DHCP binding
992  **/
993 
995  const MacAddr *macAddr)
996 {
997  uint_t i;
998  DhcpServerBinding *binding;
999 
1000  //Loop through the list of bindings
1001  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1002  {
1003  //Point to the current binding
1004  binding = &context->clientBinding[i];
1005 
1006  //Valid binding?
1007  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
1008  {
1009  //Check whether the current binding matches the specified MAC address
1010  if(macCompAddr(&binding->macAddr, macAddr))
1011  {
1012  //Return the pointer to the corresponding binding
1013  return binding;
1014  }
1015  }
1016  }
1017 
1018  //No matching binding...
1019  return NULL;
1020 }
1021 
1022 
1023 /**
1024  * @brief Search the list of bindings for a given IP address
1025  * @param[in] context Pointer to the DHCP server context
1026  * @param[in] ipAddr IP address
1027  * @return Pointer to the corresponding DHCP binding
1028  **/
1029 
1031  Ipv4Addr ipAddr)
1032 {
1033  uint_t i;
1034  DhcpServerBinding *binding;
1035 
1036  //Loop through the list of bindings
1037  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1038  {
1039  //Point to the current binding
1040  binding = &context->clientBinding[i];
1041 
1042  //Valid binding?
1043  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
1044  {
1045  //Check whether the current binding matches the specified IP address
1046  if(binding->ipAddr == ipAddr)
1047  {
1048  //Return the pointer to the corresponding binding
1049  return binding;
1050  }
1051  }
1052  }
1053 
1054  //No matching binding...
1055  return NULL;
1056 }
1057 
1058 
1059 /**
1060  * @brief Retrieve the next IP address to be used
1061  * @param[in] context Pointer to the DHCP server context
1062  * @param[out] ipAddr Next IP address to be used
1063  * @return Error code
1064  **/
1065 
1067 {
1068  uint_t i;
1069  DhcpServerBinding *binding;
1070 
1071  //Search the pool for any available IP address
1072  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1073  {
1074  //Check whether the current IP address is already allocated
1075  binding = dhcpServerFindBindingByIpAddr(context, context->nextIpAddr);
1076 
1077  //If the IP address is available, then it can be assigned to a new client
1078  if(binding == NULL)
1079  *ipAddr = context->nextIpAddr;
1080 
1081  //Compute the next IP address that will be assigned by the DHCP server
1082  if(ntohl(context->nextIpAddr) >= ntohl(context->settings.ipAddrRangeMax))
1083  {
1084  //Wrap around to the beginning of the pool
1085  context->nextIpAddr = context->settings.ipAddrRangeMin;
1086  }
1087  else
1088  {
1089  //Increment IP address
1090  context->nextIpAddr = htonl(ntohl(context->nextIpAddr) + 1);
1091  }
1092 
1093  //If the IP address is available, we are done
1094  if(binding == NULL)
1095  return NO_ERROR;
1096  }
1097 
1098  //No available addresses in the pool...
1099  return ERROR_NO_ADDRESS;
1100 }
1101 
1102 #endif
error_t udpSendDatagramEx(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, uint_t flags)
Send a UDP datagram (raw interface)
Definition: udp.c:463
void dhcpServerParseRequest(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPREQUEST message.
Definition: dhcp_server.c:526
error_t dhcpServerStop(DhcpServerContext *context)
Stop DHCP server.
Definition: dhcp_server.c:221
error_t dhcpDumpMessage(const DhcpMessage *message, size_t length)
Dump DHCP message for debugging purpose.
Definition: dhcp_debug.c:158
uint8_t length
Definition: dtls_misc.h:149
Date and time management.
@ DHCP_MESSAGE_TYPE_RELEASE
Definition: dhcp_common.h:95
Ipv4Addr defaultGateway
Default gateway.
Definition: dhcp_server.h:108
@ ERROR_NO_ADDRESS
Definition: error.h:197
DhcpServerBinding * dhcpServerFindBindingByMacAddr(DhcpServerContext *context, const MacAddr *macAddr)
Search the list of bindings for a given MAC address.
Definition: dhcp_server.c:994
void dhcpServerProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, void *param)
Process incoming DHCP message.
Definition: dhcp_server.c:305
__start_packed struct @124 DhcpOption
DHCP option.
error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type, Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length)
Send DHCP reply message.
Definition: dhcp_server.c:738
IP network address.
Definition: ip.h:71
error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr)
Retrieve the next IP address to be used.
Definition: dhcp_server.c:1066
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
#define TRUE
Definition: os_port.h:50
DHCP server settings.
Definition: dhcp_server.h:99
@ DHCP_MESSAGE_TYPE_OFFER
Definition: dhcp_common.h:90
error_t dhcpServerStart(DhcpServerContext *context)
Start DHCP server.
Definition: dhcp_server.c:194
DHCP binding.
Definition: dhcp_server.h:86
void dhcpServerParseInform(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPINFORM message.
Definition: dhcp_server.c:714
DhcpServerBinding * dhcpServerFindBindingByIpAddr(DhcpServerContext *context, Ipv4Addr ipAddr)
Search the list of bindings for a given IP address.
Definition: dhcp_server.c:1030
@ DHCP_OPT_SERVER_IDENTIFIER
Definition: dhcp_common.h:160
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ DHCP_MESSAGE_TYPE_ACK
Definition: dhcp_common.h:93
uint16_t destPort
Definition: tcp.h:303
Ipv4Addr subnetMask
Subnet mask.
Definition: dhcp_server.h:107
Ipv4Addr srcIpAddr
Definition: ipcp.h:77
MacAddr macAddr
Client's MAC address.
Definition: dhcp_server.h:88
DhcpServerSettings settings
DHCP server settings.
Definition: dhcp_server.h:119
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:77
void dhcpServerDeinit(DhcpServerContext *context)
Release DHCP server context.
Definition: dhcp_server.c:156
#define timeCompare(t1, t2)
Definition: os_port.h:42
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:239
#define DHCP_MAX_MSG_SIZE
Definition: dhcp_common.h:46
error_t dhcpServerInit(DhcpServerContext *context, const DhcpServerSettings *settings)
DHCP server initialization.
Definition: dhcp_server.c:99
#define DHCP_MAGIC_COOKIE
Definition: dhcp_common.h:51
@ DHCP_OPT_ROUTER
Definition: dhcp_common.h:109
void dhcpServerTick(DhcpServerContext *context)
DHCP server timer handler.
Definition: dhcp_server.c:251
@ DHCP_MESSAGE_TYPE_REQUEST
Definition: dhcp_common.h:91
__start_packed struct @123 DhcpMessage
DHCP message.
IP pseudo header.
Definition: ip.h:90
@ DHCP_OPT_END
Definition: dhcp_common.h:183
systime_t timestamp
Timestamp.
Definition: dhcp_server.h:91
#define FALSE
Definition: os_port.h:46
Ipv4Addr dnsServer[DHCP_SERVER_MAX_DNS_SERVERS]
DNS servers.
Definition: dhcp_server.h:109
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define htonl(value)
Definition: cpu_endian.h:393
#define HTONL(value)
Definition: cpu_endian.h:391
char_t type
error_t
Error codes.
Definition: error.h:42
@ DHCP_MESSAGE_TYPE_DISCOVER
Definition: dhcp_common.h:89
NetInterface * interface
Underlying network interface.
Definition: dhcp_server.h:101
Ipv4Addr ipAddr
Client's IPv4 address.
Definition: dhcp_server.h:89
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:413
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
Ipv4Addr ipAddrRangeMin
Lowest IP address in the pool that is available for dynamic address assignment.
Definition: dhcp_server.h:105
#define DHCP_MIN_MSG_SIZE
Definition: dhcp_common.h:44
bool_t running
This flag tells whether the DHCP server is running or not.
Definition: dhcp_server.h:120
void dhcpServerGetDefaultSettings(DhcpServerSettings *settings)
Initialize settings with default values.
Definition: dhcp_server.c:62
error_t udpDetachRxCallback(NetInterface *interface, uint16_t port)
Unregister user callback.
Definition: udp.c:811
#define DHCP_SERVER_DEFAULT_LEASE_TIME
Definition: dhcp_server.h:60
#define NetInterface
Definition: net.h:36
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:764
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1782
@ DHCP_OPT_IP_ADDRESS_LEASE_TIME
Definition: dhcp_common.h:157
@ DHCP_OPT_DHCP_MESSAGE_TYPE
Definition: dhcp_common.h:159
OsMutex netMutex
Definition: net.c:75
@ DHCP_FLAG_BROADCAST
Definition: dhcp_common.h:79
__start_packed struct @122 UdpHeader
UDP header.
Definitions common to DHCP client and server.
#define TRACE_INFO(...)
Definition: debug.h:94
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:697
@ DHCP_MESSAGE_TYPE_NAK
Definition: dhcp_common.h:94
#define IPV4_BROADCAST_ADDR
Definition: ipv4.h:106
#define IPV4_DEFAULT_TTL
Definition: ipv4.h:56
#define ntohs(value)
Definition: cpu_endian.h:398
@ DHCP_OPCODE_BOOTREPLY
Definition: dhcp_common.h:68
void dhcpServerParseRelease(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPRELEASE message.
Definition: dhcp_server.c:686
#define TRACE_DEBUG(...)
Definition: debug.h:106
@ DHCP_OPCODE_BOOTREQUEST
Definition: dhcp_common.h:67
uint32_t time
#define ipv4CompAddr(ipAddr1, ipAddr2)
Definition: ipv4.h:146
void dhcpServerParseDiscover(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPDISCOVER message.
Definition: dhcp_server.c:396
uint8_t n
#define DHCP_SERVER_MAX_DNS_SERVERS
Definition: dhcp_server.h:67
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
Data logging functions for debugging purpose (DHCP)
@ DHCP_MESSAGE_TYPE_DECLINE
Definition: dhcp_common.h:92
#define DHCP_SERVER_MAX_CLIENTS
Definition: dhcp_server.h:53
#define DHCP_SERVER_PORT
Definition: dhcp_common.h:40
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
void dhcpServerParseDecline(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPDECLINE message.
Definition: dhcp_server.c:646
@ DHCP_OPT_SUBNET_MASK
Definition: dhcp_common.h:107
bool_t rapidCommit
Quick configuration using rapid commit.
Definition: dhcp_server.h:103
@ DHCP_OPT_DNS_SERVER
Definition: dhcp_common.h:112
uint_t ipAddrIndex
Index of the IP address assigned to the DHCP server.
Definition: dhcp_server.h:102
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:107
uint8_t message[]
Definition: chap.h:152
DHCP server context.
Definition: dhcp_server.h:117
DhcpServerBinding clientBinding[DHCP_SERVER_MAX_CLIENTS]
List of bindings.
Definition: dhcp_server.h:122
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:142
systime_t dhcpServerTickCounter
Definition: dhcp_server.c:54
Ipv4Addr ipAddrRangeMax
Highest IP address in the pool that is available for dynamic address assignment.
Definition: dhcp_server.h:106
bool_t validLease
Valid lease.
Definition: dhcp_server.h:90
uint32_t leaseTime
Lease time, in seconds, assigned to the DHCP clients.
Definition: dhcp_server.h:104
#define MAX_DELAY
Definition: os_port.h:76
DhcpServerBinding * dhcpServerCreateBinding(DhcpServerContext *context)
Create a new binding.
Definition: dhcp_server.c:930
#define DHCP_HARDWARE_TYPE_ETH
Definition: dhcp_common.h:49
uint8_t value[]
Definition: dtls_misc.h:150
#define PRIuSIZE
Definition: compiler_port.h:78
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
#define DHCP_CLIENT_PORT
Definition: dhcp_common.h:41
@ DHCP_OPT_REQUESTED_IP_ADDRESS
Definition: dhcp_common.h:156
DHCP server (Dynamic Host Configuration Protocol)
uint32_t systime_t
Definition: compiler_port.h:46
uint8_t ipAddr[4]
Definition: mib_common.h:187
@ DHCP_MESSAGE_TYPE_INFORM
Definition: dhcp_common.h:96
#define ntohl(value)
Definition: cpu_endian.h:399
const MacAddr MAC_UNSPECIFIED_ADDR
Definition: ethernet.c:56
Ipv4Addr nextIpAddr
Next IP address to be assigned.
Definition: dhcp_server.h:121
@ NO_ERROR
Success.
Definition: error.h:44
void dhcpAddOption(DhcpMessage *message, uint8_t optionCode, const void *optionValue, size_t optionLen)
Append an option to a DHCP message.
Definition: dhcp_common.c:51
Debugging facilities.
DhcpOption * dhcpGetOption(const DhcpMessage *message, size_t length, uint8_t optionCode)
Find the specified option in a DHCP message.
Definition: dhcp_common.c:108
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:104
__start_packed struct @108 MacAddr
MAC address.
systime_t osGetSystemTime(void)
Retrieve system time.
Ipv4Addr destIpAddr
Definition: ipcp.h:78