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-2020 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.8
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  osMemset(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  osMemset(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  //Make sure the DHCP server context is valid
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  //Make sure the DHCP server context is valid
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] ancillary Additional options passed to the stack along with
303  * the packet
304  * @param[in] param Pointer to the DHCP server context
305  **/
306 
308  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
309  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
310  void *param)
311 {
312  size_t length;
313  DhcpServerContext *context;
315  DhcpOption *option;
316 
317  //Point to the DHCP server context
318  context = (DhcpServerContext *) param;
319 
320  //Retrieve the length of the DHCP message
321  length = netBufferGetLength(buffer) - offset;
322 
323  //Make sure the DHCP message is valid
324  if(length < sizeof(DhcpMessage))
325  return;
327  return;
328 
329  //Point to the beginning of the DHCP message
330  message = netBufferAt(buffer, offset);
331  //Sanity check
332  if(message == NULL)
333  return;
334 
335  //Debug message
336  TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
338 
339  //Dump the contents of the message for debugging purpose
341 
342  //Check opcode
344  return;
345  //Enforce hardware type
346  if(message->htype != DHCP_HARDWARE_TYPE_ETH)
347  return;
348  //Check the length of the hardware address
349  if(message->hlen != sizeof(MacAddr))
350  return;
351  //Check magic cookie
352  if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
353  return;
354 
355  //Retrieve DHCP Message Type option
357 
358  //Failed to retrieve specified option?
359  if(option == NULL || option->length != 1)
360  return;
361 
362  //Check message type
363  switch(option->value[0])
364  {
366  //Parse DHCPDISCOVER message
368  break;
370  //Parse DHCPREQUEST message
372  break;
374  //Parse DHCPDECLINE message
376  break;
378  //Parse DHCPRELEASE message
380  break;
382  //Parse DHCPINFORM message
384  break;
385  default:
386  //Silently drop incoming message
387  break;
388  }
389 }
390 
391 
392 /**
393  * @brief Parse DHCPDISCOVER message
394  * @param[in] context Pointer to the DHCP server context
395  * @param[in] message Pointer to the incoming DHCP message
396  * @param[in] length Length of the incoming message to parse
397  **/
398 
400  const DhcpMessage *message, size_t length)
401 {
402  error_t error;
403  uint_t i;
404  NetInterface *interface;
405  Ipv4Addr requestedIpAddr;
406  DhcpOption *option;
407  DhcpServerBinding *binding;
408 
409  //Point to the underlying network interface
410  interface = context->settings.interface;
411  //Index of the IP address assigned to the DHCP server
412  i = context->settings.ipAddrIndex;
413 
414  //Retrieve Server Identifier option
416 
417  //Option found?
418  if(option != NULL && option->length == 4)
419  {
420  //Unexpected server identifier?
421  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
422  return;
423  }
424 
425  //Retrieve Requested IP Address option
427 
428  //The client may include the 'requested IP address' option to suggest
429  //that a particular IP address be assigned
430  if(option != NULL && option->length == 4)
431  ipv4CopyAddr(&requestedIpAddr, option->value);
432  else
433  requestedIpAddr = IPV4_UNSPECIFIED_ADDR;
434 
435  //Search the list for a matching binding
436  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
437 
438  //Matching binding found?
439  if(binding != NULL)
440  {
441  //Different IP address than cached?
442  if(requestedIpAddr != binding->ipAddr)
443  {
444  //Ensure the IP address is in the server's pool of available addresses
445  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
446  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
447  {
448  //Make sure the IP address is not already allocated
449  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
450  {
451  //Record IP address
452  binding->ipAddr = requestedIpAddr;
453  //Get current time
454  binding->timestamp = osGetSystemTime();
455  }
456  }
457  }
458 
459  //Sucessful processing
460  error = NO_ERROR;
461  }
462  else
463  {
464  //Create a new binding
465  binding = dhcpServerCreateBinding(context);
466 
467  //Binding successfully created
468  if(binding != NULL)
469  {
470  //Ensure the IP address is in the server's pool of available addresses
471  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
472  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
473  {
474  //Make sure the IP address is not already allocated
475  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
476  {
477  //Record IP address
478  binding->ipAddr = requestedIpAddr;
479  //Sucessful processing
480  error = NO_ERROR;
481  }
482  else
483  {
484  //Retrieve the next available IP address from the pool of addresses
485  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
486  }
487  }
488  else
489  {
490  //Retrieve the next available IP address from the pool of addresses
491  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
492  }
493 
494  //Check status code
495  if(!error)
496  {
497  //Record MAC address
498  binding->macAddr = message->chaddr;
499  //Get current time
500  binding->timestamp = osGetSystemTime();
501  }
502  }
503  else
504  {
505  //Failed to create a new binding
506  error = ERROR_FAILURE;
507  }
508  }
509 
510  //Check status code
511  if(!error)
512  {
513  //The server responds with a DHCPOFFER message that includes an
514  //available network address in the 'yiaddr' field (and other
515  //configuration parameters in DHCP options)
517  binding->ipAddr, message, length);
518  }
519 }
520 
521 
522 /**
523  * @brief Parse DHCPREQUEST message
524  * @param[in] context Pointer to the DHCP server context
525  * @param[in] message Pointer to the incoming DHCP message
526  * @param[in] length Length of the incoming message to parse
527  **/
528 
530  const DhcpMessage *message, size_t length)
531 {
532  uint_t i;
533  NetInterface *interface;
534  Ipv4Addr clientIpAddr;
535  DhcpOption *option;
536  DhcpServerBinding *binding;
537 
538  //Point to the underlying network interface
539  interface = context->settings.interface;
540  //Index of the IP address assigned to the DHCP server
541  i = context->settings.ipAddrIndex;
542 
543  //Retrieve Server Identifier option
545 
546  //Option found?
547  if(option != NULL && option->length == 4)
548  {
549  //Unexpected server identifier?
550  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
551  return;
552  }
553 
554  //Check the 'ciaddr' field
555  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
556  {
557  //Save client's network address
558  clientIpAddr = message->ciaddr;
559  }
560  else
561  {
562  //Retrieve Requested IP Address option
564 
565  //Option found?
566  if(option != NULL && option->length == 4)
567  ipv4CopyAddr(&clientIpAddr, option->value);
568  else
569  clientIpAddr = IPV4_UNSPECIFIED_ADDR;
570  }
571 
572  //Valid client IP address?
573  if(clientIpAddr != IPV4_UNSPECIFIED_ADDR)
574  {
575  //Search the list for a matching binding
576  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
577 
578  //Matching binding found?
579  if(binding != NULL)
580  {
581  //Make sure the client's IP address is valid
582  if(clientIpAddr == binding->ipAddr)
583  {
584  //Commit network address
585  binding->validLease = TRUE;
586  //Save lease start time
587  binding->timestamp = osGetSystemTime();
588 
589  //The server responds with a DHCPACK message containing the
590  //configuration parameters for the requesting client
592  binding->ipAddr, message, length);
593 
594  //Exit immediately
595  return;
596  }
597  }
598  else
599  {
600  //Ensure the IP address is in the server's pool of available addresses
601  if(ntohl(clientIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
602  ntohl(clientIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
603  {
604  //Make sure the IP address is not already allocated
605  if(!dhcpServerFindBindingByIpAddr(context, clientIpAddr))
606  {
607  //Create a new binding
608  binding = dhcpServerCreateBinding(context);
609 
610  //Binding successfully created
611  if(binding != NULL)
612  {
613  //Record MAC address
614  binding->macAddr = message->chaddr;
615  //Record IP address
616  binding->ipAddr = clientIpAddr;
617  //Commit network address
618  binding->validLease = TRUE;
619  //Get current time
620  binding->timestamp = osGetSystemTime();
621 
622  //The server responds with a DHCPACK message containing the
623  //configuration parameters for the requesting client
625  binding->ipAddr, message, length);
626 
627  //Exit immediately
628  return;
629  }
630  }
631  }
632  }
633  }
634 
635  //If the server is unable to satisfy the DHCPREQUEST message, the
636  //server should respond with a DHCPNAK message
639 }
640 
641 
642 /**
643  * @brief Parse DHCPDECLINE message
644  * @param[in] context Pointer to the DHCP server context
645  * @param[in] message Pointer to the incoming DHCP message
646  * @param[in] length Length of the incoming message to parse
647  **/
648 
650  const DhcpMessage *message, size_t length)
651 {
652  DhcpOption *option;
653  DhcpServerBinding *binding;
654  Ipv4Addr requestedIpAddr;
655 
656  //Retrieve Requested IP Address option
658 
659  //Option found?
660  if(option != NULL && option->length == 4)
661  {
662  //Copy the requested IP address
663  ipv4CopyAddr(&requestedIpAddr, option->value);
664 
665  //Search the list for a matching binding
666  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
667 
668  //Matching binding found?
669  if(binding != NULL)
670  {
671  //Check the IP address against the requested IP address
672  if(binding->ipAddr == requestedIpAddr)
673  {
674  //Remote the binding from the list
675  osMemset(binding, 0, sizeof(DhcpServerBinding));
676  }
677  }
678  }
679 }
680 
681 
682 /**
683  * @brief Parse DHCPRELEASE message
684  * @param[in] context Pointer to the DHCP server context
685  * @param[in] message Pointer to the incoming DHCP message
686  * @param[in] length Length of the incoming message to parse
687  **/
688 
690  const DhcpMessage *message, size_t length)
691 {
692  DhcpServerBinding *binding;
693 
694  //Search the list for a matching binding
695  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
696 
697  //Matching binding found?
698  if(binding != NULL)
699  {
700  //Check the IP address against the client IP address
701  if(binding->ipAddr == message->ciaddr)
702  {
703  //Release the network address and cancel remaining lease
704  binding->validLease = FALSE;
705  }
706  }
707 }
708 
709 
710 /**
711  * @brief Parse DHCPINFORM message
712  * @param[in] context Pointer to the DHCP server context
713  * @param[in] message Pointer to the incoming DHCP message
714  * @param[in] length Length of the incoming message to parse
715  **/
716 
718  const DhcpMessage *message, size_t length)
719 {
720  //Make sure the client IP address is valid
721  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
722  {
723  //Servers receiving a DHCPINFORM message construct a DHCPACK message
724  //with any local configuration parameters appropriate for the client
727  }
728 }
729 
730 
731 /**
732  * @brief Send DHCP reply message
733  * @param[in] context Pointer to the DHCP server context
734  * @param[in] type DHCP message type (DHCPOFFER, DHCPACK or DHCPNAK)
735  * @param[in] yourIpAddr The IP address to be placed in the 'yiaddr' field
736  * @param[in] request Pointer to DHCP message received from the client
737  * @param[in] length Length of the DHCP message received from the client
738  * @return Error code
739  **/
740 
742  Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length)
743 {
744  error_t error;
745  uint_t i;
746  uint_t n;
747  uint32_t value;
748  size_t offset;
749  NetBuffer *buffer;
750  NetInterface *interface;
751  DhcpMessage *reply;
754  uint16_t destPort;
755  NetTxAncillary ancillary;
756 
757  //Point to the underlying network interface
758  interface = context->settings.interface;
759  //Index of the IP address assigned to the DHCP server
760  i = context->settings.ipAddrIndex;
761 
762  //Allocate a memory buffer to hold the DHCP message
763  buffer = udpAllocBuffer(DHCP_MIN_MSG_SIZE, &offset);
764  //Failed to allocate buffer?
765  if(buffer == NULL)
766  return ERROR_OUT_OF_MEMORY;
767 
768  //Point to the beginning of the DHCP message
769  reply = netBufferAt(buffer, offset);
770  //Clear memory buffer contents
771  osMemset(reply, 0, DHCP_MIN_MSG_SIZE);
772 
773  //Format DHCP reply message
774  reply->op = DHCP_OPCODE_BOOTREPLY;
775  reply->htype = DHCP_HARDWARE_TYPE_ETH;
776  reply->hlen = sizeof(MacAddr);
777  reply->xid = request->xid;
778  reply->secs = 0;
779  reply->flags = request->flags;
780  reply->ciaddr = IPV4_UNSPECIFIED_ADDR;
781  reply->yiaddr = yourIpAddr;
782  reply->siaddr = IPV4_UNSPECIFIED_ADDR;
783  reply->giaddr = request->giaddr;
784  reply->chaddr = request->chaddr;
785 
786  //Write magic cookie before setting any option
787  reply->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
788  //Properly terminate options field
789  reply->options[0] = DHCP_OPT_END;
790 
791  //Add DHCP Message Type option
793  &type, sizeof(type));
794 
795  //Add Server Identifier option
797  &interface->ipv4Context.addrList[i].addr, sizeof(Ipv4Addr));
798 
799  //DHCPOFFER or DHCPACK message?
801  {
802  //Convert the lease time to network byte order
803  value = htonl(context->settings.leaseTime);
804 
805  //When responding to a DHCPINFORM message, the server must not
806  //send a lease expiration time to the client
807  if(yourIpAddr != IPV4_UNSPECIFIED_ADDR)
808  {
809  //Add IP Address Lease Time option
811  &value, sizeof(value));
812  }
813 
814  //Add Subnet Mask option
816  {
818  &context->settings.subnetMask, sizeof(Ipv4Addr));
819  }
820 
821  //Add Router option
823  {
825  &context->settings.defaultGateway, sizeof(Ipv4Addr));
826  }
827 
828  //Retrieve the number of DNS servers
829  for(n = 0; n < DHCP_SERVER_MAX_DNS_SERVERS; n++)
830  {
831  //Check whether the current DNS server is valid
832  if(context->settings.dnsServer[n] == IPV4_UNSPECIFIED_ADDR)
833  break;
834  }
835 
836  //Add DNS Server option
837  if(n > 0)
838  {
840  context->settings.dnsServer, n * sizeof(Ipv4Addr));
841  }
842  }
843 
844  //A server with multiple network address (e.g. a multi-homed host) may
845  //use any of its network addresses in outgoing DHCP messages (refer to
846  //RFC 2131, section 4.1)
847  srcIpAddr.length = sizeof(Ipv4Addr);
848  srcIpAddr.ipv4Addr = interface->ipv4Context.addrList[i].addr;
849 
850  //Check whether the 'giaddr' field is non-zero
851  if(request->giaddr != IPV4_UNSPECIFIED_ADDR)
852  {
853  //If the 'giaddr' field in a DHCP message from a client is non-zero,
854  //the server sends any return messages to the 'DHCP server' port
856 
857  //The DHCP message is sent to the BOOTP relay agent whose address
858  //appears in 'giaddr'
859  destIpAddr.length = sizeof(Ipv4Addr);
860  destIpAddr.ipv4Addr = request->giaddr;
861  }
862  else
863  {
864  //If the 'giaddr' field in a DHCP message from a client is zero,
865  //the server sends any return messages to the 'DHCP client'
867 
868  //DHCPOFFER or DHCPACK message?
870  {
871  //Check whether the 'giaddr' field is non-zero
872  if(request->ciaddr != IPV4_UNSPECIFIED_ADDR)
873  {
874  //If the 'giaddr' field is zero and the 'ciaddr' field is nonzero,
875  //then the server unicasts DHCPOFFER and DHCPACK messages to the
876  //address in 'ciaddr'
877  destIpAddr.length = sizeof(Ipv4Addr);
878  destIpAddr.ipv4Addr = request->ciaddr;
879  }
880  else
881  {
882  //Check whether the broadcast bit is set
883  if(ntohs(request->flags) & DHCP_FLAG_BROADCAST)
884  {
885  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
886  //set, then the server broadcasts DHCPOFFER and DHCPACK messages
887  destIpAddr.length = sizeof(Ipv4Addr);
888  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
889  }
890  else
891  {
892  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
893  //not set, then the server unicasts DHCPOFFER and DHCPACK messages
894  //to the client's hardware address and 'yiaddr' address
895  destIpAddr.length = sizeof(Ipv4Addr);
896  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
897  }
898  }
899  }
900  //DHCPNAK message?
901  else
902  {
903  //In all cases, when 'giaddr' is zero, the server broadcasts any
904  //DHCPNAK messages
905  destIpAddr.length = sizeof(Ipv4Addr);
906  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
907  }
908  }
909 
910  //Debug message
911  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
913 
914  //Dump the contents of the message for debugging purpose
916 
917  //Additional options can be passed to the stack along with the packet
918  ancillary = NET_DEFAULT_TX_ANCILLARY;
919 
920  //Send DHCP reply
921  error = udpSendBuffer(interface, &srcIpAddr, DHCP_SERVER_PORT, &destIpAddr,
922  destPort, buffer, offset, &ancillary);
923 
924  //Free previously allocated memory
925  netBufferFree(buffer);
926  //Return status code
927  return error;
928 }
929 
930 
931 /**
932  * @brief Create a new binding
933  * @param[in] context Pointer to the DHCP server context
934  * @return Pointer to the newly created binding
935  **/
936 
938 {
939  uint_t i;
940  systime_t time;
941  DhcpServerBinding *binding;
942  DhcpServerBinding *oldestBinding;
943 
944  //Get current time
945  time = osGetSystemTime();
946 
947  //Keep track of the oldest binding
948  oldestBinding = NULL;
949 
950  //Loop through the list of bindings
951  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
952  {
953  //Point to the current binding
954  binding = &context->clientBinding[i];
955 
956  //Check whether the binding is available
957  if(macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
958  {
959  //Erase contents
960  osMemset(binding, 0, sizeof(DhcpServerBinding));
961  //Return a pointer to the newly created binding
962  return binding;
963  }
964  else
965  {
966  //Bindings that have been committed cannot be removed
967  if(!binding->validLease)
968  {
969  //Keep track of the oldest binding in the list
970  if(oldestBinding == NULL)
971  {
972  oldestBinding = binding;
973  }
974  else if((time - binding->timestamp) > (time - oldestBinding->timestamp))
975  {
976  oldestBinding = binding;
977  }
978  }
979  }
980  }
981 
982  //Any binding available in the list?
983  if(oldestBinding != NULL)
984  {
985  //Erase contents
986  osMemset(oldestBinding, 0, sizeof(DhcpServerBinding));
987  }
988 
989  //Return a pointer to the oldest binding
990  return oldestBinding;
991 }
992 
993 
994 /**
995  * @brief Search the list of bindings for a given MAC address
996  * @param[in] context Pointer to the DHCP server context
997  * @param[in] macAddr MAC address
998  * @return Pointer to the corresponding DHCP binding
999  **/
1000 
1002  const MacAddr *macAddr)
1003 {
1004  uint_t i;
1005  DhcpServerBinding *binding;
1006 
1007  //Loop through the list of bindings
1008  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1009  {
1010  //Point to the current binding
1011  binding = &context->clientBinding[i];
1012 
1013  //Valid binding?
1014  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
1015  {
1016  //Check whether the current binding matches the specified MAC address
1017  if(macCompAddr(&binding->macAddr, macAddr))
1018  {
1019  //Return the pointer to the corresponding binding
1020  return binding;
1021  }
1022  }
1023  }
1024 
1025  //No matching binding...
1026  return NULL;
1027 }
1028 
1029 
1030 /**
1031  * @brief Search the list of bindings for a given IP address
1032  * @param[in] context Pointer to the DHCP server context
1033  * @param[in] ipAddr IP address
1034  * @return Pointer to the corresponding DHCP binding
1035  **/
1036 
1038  Ipv4Addr ipAddr)
1039 {
1040  uint_t i;
1041  DhcpServerBinding *binding;
1042 
1043  //Loop through the list of bindings
1044  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1045  {
1046  //Point to the current binding
1047  binding = &context->clientBinding[i];
1048 
1049  //Valid binding?
1050  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
1051  {
1052  //Check whether the current binding matches the specified IP address
1053  if(binding->ipAddr == ipAddr)
1054  {
1055  //Return the pointer to the corresponding binding
1056  return binding;
1057  }
1058  }
1059  }
1060 
1061  //No matching binding...
1062  return NULL;
1063 }
1064 
1065 
1066 /**
1067  * @brief Retrieve the next IP address to be used
1068  * @param[in] context Pointer to the DHCP server context
1069  * @param[out] ipAddr Next IP address to be used
1070  * @return Error code
1071  **/
1072 
1074 {
1075  uint_t i;
1076  DhcpServerBinding *binding;
1077 
1078  //Search the pool for any available IP address
1079  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
1080  {
1081  //Check whether the current IP address is already allocated
1082  binding = dhcpServerFindBindingByIpAddr(context, context->nextIpAddr);
1083 
1084  //If the IP address is available, then it can be assigned to a new client
1085  if(binding == NULL)
1086  *ipAddr = context->nextIpAddr;
1087 
1088  //Compute the next IP address that will be assigned by the DHCP server
1089  if(ntohl(context->nextIpAddr) >= ntohl(context->settings.ipAddrRangeMax))
1090  {
1091  //Wrap around to the beginning of the pool
1092  context->nextIpAddr = context->settings.ipAddrRangeMin;
1093  }
1094  else
1095  {
1096  //Increment IP address
1097  context->nextIpAddr = htonl(ntohl(context->nextIpAddr) + 1);
1098  }
1099 
1100  //If the IP address is available, we are done
1101  if(binding == NULL)
1102  return NO_ERROR;
1103  }
1104 
1105  //No available addresses in the pool...
1106  return ERROR_NO_ADDRESS;
1107 }
1108 
1109 #endif
void dhcpServerParseRequest(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPREQUEST message.
Definition: dhcp_server.c:529
uint8_t length
Definition: coap_common.h:190
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
Date and time management.
Ipv4Addr defaultGateway
Default gateway.
Definition: dhcp_server.h:108
DhcpServerBinding * dhcpServerFindBindingByMacAddr(DhcpServerContext *context, const MacAddr *macAddr)
Search the list of bindings for a given MAC address.
Definition: dhcp_server.c:1001
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:68
error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type, Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t length)
Send DHCP reply message.
Definition: dhcp_server.c:741
#define netMutex
Definition: net_legacy.h:266
IP network address.
Definition: ip.h:78
error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr)
Retrieve the next IP address to be used.
Definition: dhcp_server.c:1073
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:88
#define TRUE
Definition: os_port.h:50
__start_packed struct @5 MacAddr
MAC address.
__start_packed struct @22 DhcpOption
DHCP option.
DHCP server settings.
Definition: dhcp_server.h:99
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:717
DhcpServerBinding * dhcpServerFindBindingByIpAddr(DhcpServerContext *context, Ipv4Addr ipAddr)
Search the list of bindings for a given IP address.
Definition: dhcp_server.c:1037
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
__start_packed struct @21 DhcpMessage
DHCP message.
#define DHCP_MAGIC_COOKIE
Definition: dhcp_common.h:51
void dhcpServerTick(DhcpServerContext *context)
DHCP server timer handler.
Definition: dhcp_server.c:251
IP pseudo header.
Definition: ip.h:97
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
Invalid parameter.
Definition: error.h:47
#define htonl(value)
Definition: cpu_endian.h:414
#define HTONL(value)
Definition: cpu_endian.h:411
char_t type
error_t
Error codes.
Definition: error.h:42
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
uint8_t value[]
Definition: tcp.h:332
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:887
#define DHCP_SERVER_DEFAULT_LEASE_TIME
Definition: dhcp_server.h:60
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:840
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
NetInterface * netGetDefaultInterface(void)
Get default network interface.
Definition: net.c:1373
error_t udpSendBuffer(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send a UDP datagram.
Definition: udp.c:512
#define NetTxAncillary
Definition: net_misc.h:36
Definitions common to DHCP client and server.
#define TRACE_INFO(...)
Definition: debug.h:95
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:773
#define IPV4_BROADCAST_ADDR
Definition: ipv4.h:106
#define ntohs(value)
Definition: cpu_endian.h:421
void dhcpServerParseRelease(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPRELEASE message.
Definition: dhcp_server.c:689
#define TRACE_DEBUG(...)
Definition: debug.h:107
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:399
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)
#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:649
bool_t rapidCommit
Quick configuration using rapid commit.
Definition: dhcp_server.h:103
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:121
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
__start_packed struct @20 UdpHeader
UDP header.
#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:937
#define DHCP_HARDWARE_TYPE_ETH
Definition: dhcp_common.h:49
#define PRIuSIZE
Definition: compiler_port.h:78
unsigned int uint_t
Definition: compiler_port.h:45
#define osMemset(p, value, length)
Definition: os_port.h:128
TCP/IP stack core.
#define DHCP_CLIENT_PORT
Definition: dhcp_common.h:41
void dhcpServerProcessMessage(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary, void *param)
Process incoming DHCP message.
Definition: dhcp_server.c:307
DHCP server (Dynamic Host Configuration Protocol)
uint32_t systime_t
Definition: compiler_port.h:46
uint8_t ipAddr[4]
Definition: mib_common.h:187
#define ntohl(value)
Definition: cpu_endian.h:422
const MacAddr MAC_UNSPECIFIED_ADDR
Definition: ethernet.c:56
Ipv4Addr nextIpAddr
Next IP address to be assigned.
Definition: dhcp_server.h:121
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
systime_t osGetSystemTime(void)
Retrieve system time.
Ipv4Addr destIpAddr
Definition: ipcp.h:78