dhcp_server_misc.c
Go to the documentation of this file.
1 /**
2  * @file dhcp_server_misc.c
3  * @brief Helper functions for DHCP server
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL DHCP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "dhcp/dhcp_server.h"
37 #include "dhcp/dhcp_server_misc.h"
38 #include "dhcp/dhcp_common.h"
39 #include "dhcp/dhcp_debug.h"
40 #include "date_time.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (IPV4_SUPPORT == ENABLED && DHCP_SERVER_SUPPORT == ENABLED)
45 
46 //Tick counter to handle periodic operations
48 
49 
50 /**
51  * @brief DHCP server timer handler
52  *
53  * This routine must be periodically called by the TCP/IP stack to
54  * manage DHCP server operation
55  *
56  * @param[in] context Pointer to the DHCP server context
57  **/
58 
60 {
61  uint_t i;
63  systime_t leaseTime;
64  DhcpServerBinding *binding;
65 
66  //Make sure the DHCP server has been properly instantiated
67  if(context == NULL)
68  return;
69 
70  //Get current time
72 
73  //Convert the lease time to milliseconds
74  if(context->settings.leaseTime < (MAX_DELAY / 1000))
75  {
76  leaseTime = context->settings.leaseTime * 1000;
77  }
78  else
79  {
80  leaseTime = MAX_DELAY;
81  }
82 
83  //Loop through the list of bindings
84  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
85  {
86  //Point to the current binding
87  binding = &context->clientBinding[i];
88 
89  //Valid binding?
90  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
91  {
92  //Check whether the network address has been committed
93  if(binding->validLease)
94  {
95  //Check whether the lease has expired
96  if(timeCompare(time, binding->timestamp + leaseTime) >= 0)
97  {
98  //The address lease is not more valid
99  binding->validLease = FALSE;
100  }
101  }
102  }
103  }
104 }
105 
106 
107 /**
108  * @brief Process incoming DHCP message
109  * @param[in] interface Underlying network interface
110  * @param[in] pseudoHeader UDP pseudo header
111  * @param[in] udpHeader UDP header
112  * @param[in] buffer Multi-part buffer containing the incoming DHCP message
113  * @param[in] offset Offset to the first byte of the DHCP message
114  * @param[in] ancillary Additional options passed to the stack along with
115  * the packet
116  * @param[in] param Pointer to the DHCP server context
117  **/
118 
120  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
121  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
122  void *param)
123 {
124  error_t error;
125  size_t length;
126  DhcpServerContext *context;
128  DhcpOption *option;
130 
131  //Point to the DHCP server context
132  context = (DhcpServerContext *) param;
133 
134  //Retrieve the length of the DHCP message
135  length = netBufferGetLength(buffer) - offset;
136 
137  //Make sure the DHCP message is valid
138  if(length < sizeof(DhcpMessage) || length > DHCP_MAX_MSG_SIZE)
139  return;
140 
141  //Point to the beginning of the DHCP message
142  message = netBufferAt(buffer, offset, length);
143  //Sanity check
144  if(message == NULL)
145  return;
146 
147  //Debug message
148  TRACE_DEBUG("\r\n%s: DHCP message received (%" PRIuSIZE " bytes)...\r\n",
150 
151  //Dump the contents of the message for debugging purpose
153 
154  //Check opcode
156  return;
157  //Enforce hardware type
158  if(message->htype != DHCP_HARDWARE_TYPE_ETH)
159  return;
160  //Check the length of the hardware address
161  if(message->hlen != sizeof(MacAddr))
162  return;
163  //Check magic cookie
164  if(message->magicCookie != HTONL(DHCP_MAGIC_COOKIE))
165  return;
166 
167  //Retrieve DHCP Message Type option
169 
170  //Failed to retrieve specified option?
171  if(option == NULL || option->length != 1)
172  return;
173 
174  //Retrieve message type
175  type = (DhcpMessageType) option->value[0];
176 
177  //Any registered callback?
178  if(context->settings.parseOptionsCallback != NULL)
179  {
180  //Invoke user callback function
181  error = context->settings.parseOptionsCallback(context, message, length,
182  type);
183  //Check status code
184  if(error)
185  return;
186  }
187 
188  //Check message type
189  switch(type)
190  {
192  //Parse DHCPDISCOVER message
194  break;
195 
197  //Parse DHCPREQUEST message
199  break;
200 
202  //Parse DHCPDECLINE message
204  break;
205 
207  //Parse DHCPRELEASE message
209  break;
210 
212  //Parse DHCPINFORM message
214  break;
215 
216  default:
217  //Silently drop incoming message
218  break;
219  }
220 }
221 
222 
223 /**
224  * @brief Parse DHCPDISCOVER message
225  * @param[in] context Pointer to the DHCP server context
226  * @param[in] message Pointer to the incoming DHCP message
227  * @param[in] length Length of the incoming message to parse
228  **/
229 
231  const DhcpMessage *message, size_t length)
232 {
233  error_t error;
234  uint_t i;
235  NetInterface *interface;
236  Ipv4Addr requestedIpAddr;
237  DhcpOption *option;
238  DhcpServerBinding *binding;
239 
240  //Point to the underlying network interface
241  interface = context->settings.interface;
242  //Index of the IP address assigned to the DHCP server
243  i = context->settings.ipAddrIndex;
244 
245  //Retrieve Server Identifier option
247 
248  //Option found?
249  if(option != NULL && option->length == 4)
250  {
251  //Unexpected server identifier?
252  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
253  return;
254  }
255 
256  //Retrieve Requested IP Address option
258 
259  //The client may include the 'requested IP address' option to suggest
260  //that a particular IP address be assigned
261  if(option != NULL && option->length == 4)
262  {
263  ipv4CopyAddr(&requestedIpAddr, option->value);
264  }
265  else
266  {
267  requestedIpAddr = IPV4_UNSPECIFIED_ADDR;
268  }
269 
270  //Search the list for a matching binding
271  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
272 
273  //Matching binding found?
274  if(binding != NULL)
275  {
276  //Different IP address than cached?
277  if(requestedIpAddr != binding->ipAddr)
278  {
279  //Ensure the IP address is in the server's pool of available addresses
280  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
281  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
282  {
283  //Make sure the IP address is not already allocated
284  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
285  {
286  //Record IP address
287  binding->ipAddr = requestedIpAddr;
288  //Get current time
289  binding->timestamp = osGetSystemTime();
290  }
291  }
292  }
293 
294  //Successful processing
295  error = NO_ERROR;
296  }
297  else
298  {
299  //Create a new binding
300  binding = dhcpServerCreateBinding(context);
301 
302  //Binding successfully created
303  if(binding != NULL)
304  {
305  //Ensure the IP address is in the server's pool of available addresses
306  if(ntohl(requestedIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
307  ntohl(requestedIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
308  {
309  //Make sure the IP address is not already allocated
310  if(!dhcpServerFindBindingByIpAddr(context, requestedIpAddr))
311  {
312  //Record IP address
313  binding->ipAddr = requestedIpAddr;
314  //Successful processing
315  error = NO_ERROR;
316  }
317  else
318  {
319  //Retrieve the next available IP address from the pool of addresses
320  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
321  }
322  }
323  else
324  {
325  //Retrieve the next available IP address from the pool of addresses
326  error = dhcpServerGetNextIpAddr(context, &binding->ipAddr);
327  }
328 
329  //Check status code
330  if(!error)
331  {
332  //Record MAC address
333  binding->macAddr = message->chaddr;
334  //Get current time
335  binding->timestamp = osGetSystemTime();
336  }
337  }
338  else
339  {
340  //Failed to create a new binding
341  error = ERROR_FAILURE;
342  }
343  }
344 
345  //Check status code
346  if(!error)
347  {
348  //The server responds with a DHCPOFFER message that includes an
349  //available network address in the 'yiaddr' field (and other
350  //configuration parameters in DHCP options)
352  message, length);
353  }
354 }
355 
356 
357 /**
358  * @brief Parse DHCPREQUEST message
359  * @param[in] context Pointer to the DHCP server context
360  * @param[in] message Pointer to the incoming DHCP message
361  * @param[in] length Length of the incoming message to parse
362  **/
363 
365  const DhcpMessage *message, size_t length)
366 {
367  uint_t i;
368  NetInterface *interface;
369  Ipv4Addr clientIpAddr;
370  DhcpOption *option;
371  DhcpServerBinding *binding;
372 
373  //Point to the underlying network interface
374  interface = context->settings.interface;
375  //Index of the IP address assigned to the DHCP server
376  i = context->settings.ipAddrIndex;
377 
378  //Retrieve Server Identifier option
380 
381  //Option found?
382  if(option != NULL && option->length == 4)
383  {
384  //Unexpected server identifier?
385  if(!ipv4CompAddr(option->value, &interface->ipv4Context.addrList[i].addr))
386  return;
387  }
388 
389  //Check the 'ciaddr' field
390  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
391  {
392  //Save client's network address
393  clientIpAddr = message->ciaddr;
394  }
395  else
396  {
397  //Retrieve Requested IP Address option
399 
400  //Option found?
401  if(option != NULL && option->length == 4)
402  {
403  ipv4CopyAddr(&clientIpAddr, option->value);
404  }
405  else
406  {
407  clientIpAddr = IPV4_UNSPECIFIED_ADDR;
408  }
409  }
410 
411  //Valid client IP address?
412  if(clientIpAddr != IPV4_UNSPECIFIED_ADDR)
413  {
414  //Search the list for a matching binding
415  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
416 
417  //Matching binding found?
418  if(binding != NULL)
419  {
420  //Make sure the client's IP address is valid
421  if(clientIpAddr == binding->ipAddr)
422  {
423  //Commit network address
424  binding->validLease = TRUE;
425  //Save lease start time
426  binding->timestamp = osGetSystemTime();
427 
428  //The server responds with a DHCPACK message containing the
429  //configuration parameters for the requesting client
431  binding->ipAddr, message, length);
432 
433  //Exit immediately
434  return;
435  }
436  }
437  else
438  {
439  //Ensure the IP address is in the server's pool of available addresses
440  if(ntohl(clientIpAddr) >= ntohl(context->settings.ipAddrRangeMin) &&
441  ntohl(clientIpAddr) <= ntohl(context->settings.ipAddrRangeMax))
442  {
443  //Make sure the IP address is not already allocated
444  if(!dhcpServerFindBindingByIpAddr(context, clientIpAddr))
445  {
446  //Create a new binding
447  binding = dhcpServerCreateBinding(context);
448 
449  //Binding successfully created
450  if(binding != NULL)
451  {
452  //Record MAC address
453  binding->macAddr = message->chaddr;
454  //Record IP address
455  binding->ipAddr = clientIpAddr;
456  //Commit network address
457  binding->validLease = TRUE;
458  //Get current time
459  binding->timestamp = osGetSystemTime();
460 
461  //The server responds with a DHCPACK message containing the
462  //configuration parameters for the requesting client
464  binding->ipAddr, message, length);
465 
466  //Exit immediately
467  return;
468  }
469  }
470  }
471  }
472  }
473 
474  //If the server is unable to satisfy the DHCPREQUEST message, the
475  //server should respond with a DHCPNAK message
477  message, length);
478 }
479 
480 
481 /**
482  * @brief Parse DHCPDECLINE message
483  * @param[in] context Pointer to the DHCP server context
484  * @param[in] message Pointer to the incoming DHCP message
485  * @param[in] length Length of the incoming message to parse
486  **/
487 
489  const DhcpMessage *message, size_t length)
490 {
491  DhcpOption *option;
492  DhcpServerBinding *binding;
493  Ipv4Addr requestedIpAddr;
494 
495  //Retrieve Requested IP Address option
497 
498  //Option found?
499  if(option != NULL && option->length == 4)
500  {
501  //Copy the requested IP address
502  ipv4CopyAddr(&requestedIpAddr, option->value);
503 
504  //Search the list for a matching binding
505  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
506 
507  //Matching binding found?
508  if(binding != NULL)
509  {
510  //Check the IP address against the requested IP address
511  if(binding->ipAddr == requestedIpAddr)
512  {
513  //Remote the binding from the list
514  osMemset(binding, 0, sizeof(DhcpServerBinding));
515  }
516  }
517  }
518 }
519 
520 
521 /**
522  * @brief Parse DHCPRELEASE message
523  * @param[in] context Pointer to the DHCP server context
524  * @param[in] message Pointer to the incoming DHCP message
525  * @param[in] length Length of the incoming message to parse
526  **/
527 
529  const DhcpMessage *message, size_t length)
530 {
531  DhcpServerBinding *binding;
532 
533  //Search the list for a matching binding
534  binding = dhcpServerFindBindingByMacAddr(context, &message->chaddr);
535 
536  //Matching binding found?
537  if(binding != NULL)
538  {
539  //Check the IP address against the client IP address
540  if(binding->ipAddr == message->ciaddr)
541  {
542  //Release the network address and cancel remaining lease
543  binding->validLease = FALSE;
544  }
545  }
546 }
547 
548 
549 /**
550  * @brief Parse DHCPINFORM message
551  * @param[in] context Pointer to the DHCP server context
552  * @param[in] message Pointer to the incoming DHCP message
553  * @param[in] length Length of the incoming message to parse
554  **/
555 
557  const DhcpMessage *message, size_t length)
558 {
559  //Make sure the client IP address is valid
560  if(message->ciaddr != IPV4_UNSPECIFIED_ADDR)
561  {
562  //Servers receiving a DHCPINFORM message construct a DHCPACK message
563  //with any local configuration parameters appropriate for the client
565  message, length);
566  }
567 }
568 
569 
570 /**
571  * @brief Send DHCP reply message
572  * @param[in] context Pointer to the DHCP server context
573  * @param[in] type DHCP message type (DHCPOFFER, DHCPACK or DHCPNAK)
574  * @param[in] yourIpAddr The IP address to be placed in the 'yiaddr' field
575  * @param[in] request Pointer to DHCP message received from the client
576  * @param[in] requestLen Length of the DHCP message received from the client
577  * @return Error code
578  **/
579 
581  Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t requestLen)
582 {
583  error_t error;
584  uint_t i;
585  uint_t n;
586  uint32_t value;
587  size_t offset;
588  size_t replyLen;
589  NetBuffer *buffer;
590  NetInterface *interface;
591  DhcpMessage *reply;
594  uint16_t destPort;
595  NetTxAncillary ancillary;
596 
597  //Point to the underlying network interface
598  interface = context->settings.interface;
599  //Index of the IP address assigned to the DHCP server
600  i = context->settings.ipAddrIndex;
601 
602  //Allocate a memory buffer to hold the DHCP message
603  buffer = udpAllocBuffer(DHCP_MAX_MSG_SIZE, &offset);
604  //Failed to allocate buffer?
605  if(buffer == NULL)
606  return ERROR_OUT_OF_MEMORY;
607 
608  //Point to the beginning of the DHCP message
609  reply = netBufferAt(buffer, offset, 0);
610 
611  //Clear memory buffer contents
612  osMemset(reply, 0, DHCP_MAX_MSG_SIZE);
613 
614  //Format DHCP reply message
615  reply->op = DHCP_OPCODE_BOOTREPLY;
616  reply->htype = DHCP_HARDWARE_TYPE_ETH;
617  reply->hlen = sizeof(MacAddr);
618  reply->xid = request->xid;
619  reply->secs = 0;
620  reply->flags = request->flags;
621  reply->ciaddr = IPV4_UNSPECIFIED_ADDR;
622  reply->yiaddr = yourIpAddr;
623  reply->siaddr = IPV4_UNSPECIFIED_ADDR;
624  reply->giaddr = request->giaddr;
625  reply->chaddr = request->chaddr;
626 
627  //Write magic cookie before setting any option
628  reply->magicCookie = HTONL(DHCP_MAGIC_COOKIE);
629  //Properly terminate options field
630  reply->options[0] = DHCP_OPT_END;
631 
632  //Total length of the DHCP message
633  replyLen = sizeof(DhcpMessage) + sizeof(uint8_t);
634 
635  //Add DHCP Message Type option
636  dhcpAddOption(reply, &replyLen, DHCP_OPT_DHCP_MESSAGE_TYPE,
637  &type, sizeof(type));
638 
639  //Add Server Identifier option
640  dhcpAddOption(reply, &replyLen, DHCP_OPT_SERVER_ID,
641  &interface->ipv4Context.addrList[i].addr, sizeof(Ipv4Addr));
642 
643  //DHCPOFFER or DHCPACK message?
645  {
646  //Convert the lease time to network byte order
647  value = htonl(context->settings.leaseTime);
648 
649  //When responding to a DHCPINFORM message, the server must not
650  //send a lease expiration time to the client
651  if(yourIpAddr != IPV4_UNSPECIFIED_ADDR)
652  {
653  //Add IP Address Lease Time option
655  &value, sizeof(value));
656  }
657 
658  //Add Subnet Mask option
659  if(context->settings.subnetMask != IPV4_UNSPECIFIED_ADDR)
660  {
661  dhcpAddOption(reply, &replyLen, DHCP_OPT_SUBNET_MASK,
662  &context->settings.subnetMask, sizeof(Ipv4Addr));
663  }
664 
665  //Add Router option
666  if(context->settings.defaultGateway != IPV4_UNSPECIFIED_ADDR)
667  {
668  dhcpAddOption(reply, &replyLen, DHCP_OPT_ROUTER,
669  &context->settings.defaultGateway, sizeof(Ipv4Addr));
670  }
671 
672  //Retrieve the number of DNS servers
673  for(n = 0; n < DHCP_SERVER_MAX_DNS_SERVERS; n++)
674  {
675  //Check whether the current DNS server is valid
676  if(context->settings.dnsServer[n] == IPV4_UNSPECIFIED_ADDR)
677  break;
678  }
679 
680  //Add DNS Server option
681  if(n > 0)
682  {
683  dhcpAddOption(reply, &replyLen, DHCP_OPT_DNS_SERVER,
684  context->settings.dnsServer, n * sizeof(Ipv4Addr));
685  }
686  }
687 
688  //Any registered callback?
689  if(context->settings.addOptionsCallback != NULL)
690  {
691  //Invoke user callback function
692  context->settings.addOptionsCallback(context, reply, &replyLen,
694  }
695 
696  //The minimum length of BOOTP frames is 300 octets (refer to RFC 951,
697  //section 3)
698  replyLen = MAX(replyLen, DHCP_MIN_MSG_SIZE);
699 
700  //Adjust the length of the multi-part buffer
701  netBufferSetLength(buffer, offset + replyLen);
702 
703  //Additional options can be passed to the stack along with the packet
704  ancillary = NET_DEFAULT_TX_ANCILLARY;
705 
706  //A server with multiple network address (e.g. a multi-homed host) may
707  //use any of its network addresses in outgoing DHCP messages (refer to
708  //RFC 2131, section 4.1)
709  srcIpAddr.length = sizeof(Ipv4Addr);
710  srcIpAddr.ipv4Addr = interface->ipv4Context.addrList[i].addr;
711 
712  //Check whether the 'giaddr' field is non-zero
713  if(request->giaddr != IPV4_UNSPECIFIED_ADDR)
714  {
715  //If the 'giaddr' field in a DHCP message from a client is non-zero,
716  //the server sends any return messages to the 'DHCP server' port
718 
719  //The DHCP message is sent to the BOOTP relay agent whose address
720  //appears in 'giaddr'
721  destIpAddr.length = sizeof(Ipv4Addr);
722  destIpAddr.ipv4Addr = request->giaddr;
723  }
724  else
725  {
726  //If the 'giaddr' field in a DHCP message from a client is zero,
727  //the server sends any return messages to the 'DHCP client'
729 
730  //DHCPOFFER or DHCPACK message?
732  {
733  //Check whether the 'ciaddr' field is non-zero
734  if(request->ciaddr != IPV4_UNSPECIFIED_ADDR)
735  {
736  //If the 'giaddr' field is zero and the 'ciaddr' field is nonzero,
737  //then the server unicasts DHCPOFFER and DHCPACK messages to the
738  //address in 'ciaddr'
739  destIpAddr.length = sizeof(Ipv4Addr);
740  destIpAddr.ipv4Addr = request->ciaddr;
741  }
742  else
743  {
744  //Check whether the broadcast bit is set
745  if(ntohs(request->flags) & DHCP_FLAG_BROADCAST)
746  {
747  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
748  //set, then the server broadcasts DHCPOFFER and DHCPACK messages
749  destIpAddr.length = sizeof(Ipv4Addr);
750  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
751  }
752  else
753  {
754  //If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
755  //not set, then the server unicasts DHCPOFFER and DHCPACK messages
756  //to the client's hardware address and 'yiaddr' address
757  ancillary.destMacAddr = request->chaddr;
758  destIpAddr.length = sizeof(Ipv4Addr);
759  destIpAddr.ipv4Addr = yourIpAddr;
760  }
761  }
762  }
763  //DHCPNAK message?
764  else
765  {
766  //In all cases, when 'giaddr' is zero, the server broadcasts any
767  //DHCPNAK messages
768  destIpAddr.length = sizeof(Ipv4Addr);
769  destIpAddr.ipv4Addr = IPV4_BROADCAST_ADDR;
770  }
771  }
772 
773  //Debug message
774  TRACE_DEBUG("\r\n%s: Sending DHCP message (%" PRIuSIZE " bytes)...\r\n",
775  formatSystemTime(osGetSystemTime(), NULL), replyLen);
776 
777  //Dump the contents of the message for debugging purpose
778  dhcpDumpMessage(reply, replyLen);
779 
780  //Send DHCP reply
781  error = udpSendBuffer(interface, &srcIpAddr, DHCP_SERVER_PORT, &destIpAddr,
782  destPort, buffer, offset, &ancillary);
783 
784  //Free previously allocated memory
785  netBufferFree(buffer);
786 
787  //Return status code
788  return error;
789 }
790 
791 
792 /**
793  * @brief Create a new binding
794  * @param[in] context Pointer to the DHCP server context
795  * @return Pointer to the newly created binding
796  **/
797 
799 {
800  uint_t i;
801  systime_t time;
802  DhcpServerBinding *binding;
803  DhcpServerBinding *oldestBinding;
804 
805  //Get current time
806  time = osGetSystemTime();
807 
808  //Keep track of the oldest binding
809  oldestBinding = NULL;
810 
811  //Loop through the list of bindings
812  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
813  {
814  //Point to the current binding
815  binding = &context->clientBinding[i];
816 
817  //Check whether the binding is available
818  if(macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
819  {
820  //Erase contents
821  osMemset(binding, 0, sizeof(DhcpServerBinding));
822  //Return a pointer to the newly created binding
823  return binding;
824  }
825  else
826  {
827  //Bindings that have been committed cannot be removed
828  if(!binding->validLease)
829  {
830  //Keep track of the oldest binding in the list
831  if(oldestBinding == NULL)
832  {
833  oldestBinding = binding;
834  }
835  else if((time - binding->timestamp) > (time - oldestBinding->timestamp))
836  {
837  oldestBinding = binding;
838  }
839  }
840  }
841  }
842 
843  //Any binding available in the list?
844  if(oldestBinding != NULL)
845  {
846  //Erase contents
847  osMemset(oldestBinding, 0, sizeof(DhcpServerBinding));
848  }
849 
850  //Return a pointer to the oldest binding
851  return oldestBinding;
852 }
853 
854 
855 /**
856  * @brief Search the list of bindings for a given MAC address
857  * @param[in] context Pointer to the DHCP server context
858  * @param[in] macAddr MAC address
859  * @return Pointer to the corresponding DHCP binding
860  **/
861 
863  const MacAddr *macAddr)
864 {
865  uint_t i;
866  DhcpServerBinding *binding;
867 
868  //Loop through the list of bindings
869  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
870  {
871  //Point to the current binding
872  binding = &context->clientBinding[i];
873 
874  //Valid binding?
875  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
876  {
877  //Check whether the current binding matches the specified MAC address
878  if(macCompAddr(&binding->macAddr, macAddr))
879  {
880  //Return the pointer to the corresponding binding
881  return binding;
882  }
883  }
884  }
885 
886  //No matching binding...
887  return NULL;
888 }
889 
890 
891 /**
892  * @brief Search the list of bindings for a given IP address
893  * @param[in] context Pointer to the DHCP server context
894  * @param[in] ipAddr IP address
895  * @return Pointer to the corresponding DHCP binding
896  **/
897 
900 {
901  uint_t i;
902  DhcpServerBinding *binding;
903 
904  //Loop through the list of bindings
905  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
906  {
907  //Point to the current binding
908  binding = &context->clientBinding[i];
909 
910  //Valid binding?
911  if(!macCompAddr(&binding->macAddr, &MAC_UNSPECIFIED_ADDR))
912  {
913  //Check whether the current binding matches the specified IP address
914  if(binding->ipAddr == ipAddr)
915  {
916  //Return the pointer to the corresponding binding
917  return binding;
918  }
919  }
920  }
921 
922  //No matching binding...
923  return NULL;
924 }
925 
926 
927 /**
928  * @brief Retrieve the next IP address to be used
929  * @param[in] context Pointer to the DHCP server context
930  * @param[out] ipAddr Next IP address to be used
931  * @return Error code
932  **/
933 
935 {
936  uint_t i;
937  DhcpServerBinding *binding;
938 
939  //Search the pool for any available IP address
940  for(i = 0; i < DHCP_SERVER_MAX_CLIENTS; i++)
941  {
942  //Check whether the current IP address is already allocated
943  binding = dhcpServerFindBindingByIpAddr(context, context->nextIpAddr);
944 
945  //If the IP address is available, then it can be assigned to a new client
946  if(binding == NULL)
947  *ipAddr = context->nextIpAddr;
948 
949  //Compute the next IP address that will be assigned by the DHCP server
950  if(ntohl(context->nextIpAddr) >= ntohl(context->settings.ipAddrRangeMax))
951  {
952  //Wrap around to the beginning of the pool
953  context->nextIpAddr = context->settings.ipAddrRangeMin;
954  }
955  else
956  {
957  //Increment IP address
958  context->nextIpAddr = htonl(ntohl(context->nextIpAddr) + 1);
959  }
960 
961  //If the IP address is available, we are done
962  if(binding == NULL)
963  return NO_ERROR;
964  }
965 
966  //No available addresses in the pool
967  return ERROR_NO_ADDRESS;
968 }
969 
970 #endif
error_t dhcpDumpMessage(const DhcpMessage *message, size_t length)
Dump DHCP message for debugging purpose.
Definition: dhcp_debug.c:158
Date and time management.
@ DHCP_OPT_SERVER_ID
Definition: dhcp_common.h:160
@ ERROR_NO_ADDRESS
Definition: error.h:199
void dhcpServerParseRelease(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPRELEASE message.
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
void dhcpServerParseRequest(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPREQUEST message.
IP network address.
Definition: ip.h:90
DhcpMessageType
DHCP message types.
Definition: dhcp_common.h:88
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint8_t message[]
Definition: chap.h:154
#define TRUE
Definition: os_port.h:50
@ DHCP_OPT_REQUESTED_IP_ADDR
Definition: dhcp_common.h:156
DHCP binding.
Definition: dhcp_server.h:112
@ DHCP_MSG_TYPE_ACK
Definition: dhcp_common.h:93
uint8_t type
Definition: coap_common.h:176
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
uint16_t destPort
Definition: tcp.h:340
Ipv4Addr srcIpAddr
Definition: ipcp.h:79
MacAddr macAddr
Client's MAC address.
Definition: dhcp_server.h:113
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:77
#define timeCompare(t1, t2)
Definition: os_port.h:40
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:297
#define DHCP_MAX_MSG_SIZE
Definition: dhcp_common.h:46
#define DHCP_MAGIC_COOKIE
Definition: dhcp_common.h:51
Helper functions for DHCP server.
@ DHCP_OPT_ROUTER
Definition: dhcp_common.h:109
error_t dhcpServerGetNextIpAddr(DhcpServerContext *context, Ipv4Addr *ipAddr)
Retrieve the next IP address to be used.
IP pseudo header.
Definition: ip.h:110
@ DHCP_OPT_END
Definition: dhcp_common.h:191
error_t dhcpAddOption(DhcpMessage *message, size_t *messageLen, uint8_t optionCode, const void *optionValue, size_t optionLen)
Append an option to a DHCP message.
Definition: dhcp_common.c:56
systime_t timestamp
Timestamp.
Definition: dhcp_server.h:116
#define FALSE
Definition: os_port.h:46
systime_t dhcpServerTickCounter
#define htonl(value)
Definition: cpu_endian.h:414
DhcpServerBinding * dhcpServerFindBindingByMacAddr(DhcpServerContext *context, const MacAddr *macAddr)
Search the list of bindings for a given MAC address.
DhcpServerBinding * dhcpServerCreateBinding(DhcpServerContext *context)
Create a new binding.
error_t dhcpServerSendReply(DhcpServerContext *context, uint8_t type, Ipv4Addr yourIpAddr, const DhcpMessage *request, size_t requestLen)
Send DHCP reply message.
#define HTONL(value)
Definition: cpu_endian.h:411
error_t
Error codes.
Definition: error.h:43
void dhcpServerParseInform(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPINFORM message.
Ipv4Addr ipAddr
Client's IPv4 address.
Definition: dhcp_server.h:114
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define DHCP_MIN_MSG_SIZE
Definition: dhcp_common.h:44
#define NetRxAncillary
Definition: net_misc.h:40
#define NetInterface
Definition: net.h:36
void dhcpServerParseDiscover(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPDISCOVER message.
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
@ DHCP_OPT_IP_ADDRESS_LEASE_TIME
Definition: dhcp_common.h:157
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:658
DhcpMessage
Definition: dhcp_common.h:226
@ DHCP_OPT_DHCP_MESSAGE_TYPE
Definition: dhcp_common.h:159
#define NetTxAncillary
Definition: net_misc.h:36
@ DHCP_MSG_TYPE_DECLINE
Definition: dhcp_common.h:92
@ DHCP_FLAG_BROADCAST
Definition: dhcp_common.h:79
Definitions common to DHCP client and server.
uint8_t length
Definition: tcp.h:368
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:948
#define IPV4_BROADCAST_ADDR
Definition: ipv4.h:119
DhcpServerBinding * dhcpServerFindBindingByIpAddr(DhcpServerContext *context, Ipv4Addr ipAddr)
Search the list of bindings for a given IP address.
MacAddr
Definition: ethernet.h:195
@ DHCP_MSG_TYPE_INFORM
Definition: dhcp_common.h:96
UdpHeader
Definition: udp.h:85
uint32_t systime_t
System time.
#define ntohs(value)
Definition: cpu_endian.h:421
@ DHCP_OPCODE_BOOTREPLY
Definition: dhcp_common.h:68
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define MAX(a, b)
Definition: os_port.h:67
@ DHCP_OPCODE_BOOTREQUEST
Definition: dhcp_common.h:67
uint32_t time
#define ipv4CompAddr(ipAddr1, ipAddr2)
Definition: ipv4.h:159
uint8_t n
#define DHCP_SERVER_MAX_DNS_SERVERS
Definition: dhcp_server.h:67
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
@ DHCP_OPT_SUBNET_MASK
Definition: dhcp_common.h:107
@ DHCP_OPT_DNS_SERVER
Definition: dhcp_common.h:112
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:322
uint8_t value[]
Definition: tcp.h:369
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
@ DHCP_MSG_TYPE_OFFER
Definition: dhcp_common.h:90
DhcpOption
Definition: dhcp_common.h:238
@ DHCP_MSG_TYPE_REQUEST
Definition: dhcp_common.h:91
@ DHCP_MSG_TYPE_NAK
Definition: dhcp_common.h:94
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:155
void * netBufferAt(const NetBuffer *buffer, size_t offset, size_t length)
Returns a pointer to a data segment.
Definition: net_mem.c:418
Ipv4Addr ipAddr
Definition: ipcp.h:105
bool_t validLease
Valid lease.
Definition: dhcp_server.h:115
#define MAX_DELAY
Definition: os_port.h:77
#define DHCP_HARDWARE_TYPE_ETH
Definition: dhcp_common.h:49
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
@ DHCP_MSG_TYPE_DISCOVER
Definition: dhcp_common.h:89
#define osMemset(p, value, length)
Definition: os_port.h:135
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.
DHCP server (Dynamic Host Configuration Protocol)
#define DhcpServerContext
Definition: dhcp_server.h:79
@ DHCP_MSG_TYPE_RELEASE
Definition: dhcp_common.h:95
#define ntohl(value)
Definition: cpu_endian.h:422
void dhcpServerParseDecline(DhcpServerContext *context, const DhcpMessage *message, size_t length)
Parse DHCPDECLINE message.
const MacAddr MAC_UNSPECIFIED_ADDR
Definition: ethernet.c:53
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
DhcpOption * dhcpGetOption(const DhcpMessage *message, size_t length, uint8_t optionCode)
Search a DHCP message for a given option.
Definition: dhcp_common.c:118
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:117
void dhcpServerTick(DhcpServerContext *context)
DHCP server timer handler.
systime_t osGetSystemTime(void)
Retrieve system time.
Ipv4Addr destIpAddr
Definition: ipcp.h:80