3      Copyright (C) Dean Camera, 2011. 
   5   dean [at] fourwalledcubicle [dot] com 
  10   Copyright 2011  Dean Camera (dean [at] fourwalledcubicle [dot] com) 
  12   Permission to use, copy, modify, distribute, and sell this 
  13   software and its documentation for any purpose is hereby granted 
  14   without fee, provided that the above copyright notice appear in 
  15   all copies and that both that the copyright notice and this 
  16   permission notice and warranty disclaimer appear in supporting 
  17   documentation, and that the name of the author not be used in 
  18   advertising or publicity pertaining to distribution of the 
  19   software without specific, written prior permission. 
  21   The author disclaim all warranties with regard to this 
  22   software, including all implied warranties of merchantability 
  23   and fitness.  In no event shall the author be liable for any 
  24   special, indirect or consequential damages or any damages 
  25   whatsoever resulting from loss of use, data or profits, whether 
  26   in an action of contract, negligence or other tortious action, 
  27   arising out of or in connection with the use or performance of 
  31 #if defined(ENABLE_DHCP_SERVER) || defined(__DOXYGEN__) 
  35  *  DHCP Server Application. When connected to the uIP stack, this will send IP configuration settings to a 
  36  *  DHCP client on the network. 
  39 #define  INCLUDE_FROM_DHCPSERVERAPP_C 
  40 #include "DHCPServerApp.h" 
  42 struct uip_conn
* BroadcastConnection
; 
  44 uint8_t LeasedIPs
[255 / 8]; 
  46 /** Initialization function for the DHCP server. */ 
  47 void DHCPServerApp_Init(void) 
  49         /* Listen on port 67 for DHCP server connections from hosts */ 
  50         uip_listen(HTONS(DHCP_SERVER_PORT
)); 
  52         /* Create a new UDP connection to the DHCP server port for the DHCP solicitation */ 
  53         struct uip_udp_conn
* BroadcastConnection 
= uip_udp_new(&uip_broadcast_addr
, HTONS(DHCP_CLIENT_PORT
)); 
  55         /* If the connection was successfully created, bind it to the local DHCP client port */ 
  56         if (BroadcastConnection 
!= NULL
) 
  57           uip_udp_bind(BroadcastConnection
, HTONS(DHCP_SERVER_PORT
)); 
  59         /* Set all IP addresses as unleased */ 
  60         memset(LeasedIPs
, 0x00, sizeof(LeasedIPs
)); 
  63 /** uIP stack application callback for the DHCP server. This function must be called each time the TCP/IP stack 
  64  *  needs a UDP packet to be processed. 
  66 void DHCPServerApp_Callback(void) 
  68         DHCP_Header_t
* const AppData     
= (DHCP_Header_t
*)uip_appdata
; 
  69         uint16_t             AppDataSize 
= 0; 
  71         /* Only process when new data arrives - don't retransmit lost packets */ 
  74                 /* Get the DHCP message type (if present), otherwise early-abort */ 
  75                 uint8_t DHCPMessageType
; 
  76                 if (!(DHCPCommon_GetOption(AppData
->Options
, DHCP_OPTION_MSG_TYPE
, &DHCPMessageType
))) 
  79                 uip_ipaddr_t        Netmask
, GatewayIPAddress
, PreferredClientIP
; 
  80                 struct uip_eth_addr RemoteMACAddress
; 
  81                 uint32_t            TransactionID
; 
  83                 /* Get configured network mask, gateway IP and extract out DHCP transaction ID and remote IP */ 
  84                 uip_getnetmask(&Netmask
); 
  85                 uip_getdraddr(&GatewayIPAddress
); 
  86                 memcpy(&RemoteMACAddress
, &AppData
->ClientHardwareAddress
, sizeof(struct uip_eth_addr
)); 
  87                 TransactionID 
= AppData
->TransactionID
; 
  89                 /* Try to extract out the client's preferred IP address if it is indicated in the packet */ 
  90                 if (!(DHCPCommon_GetOption(AppData
->Options
, DHCP_OPTION_REQ_IPADDR
, &PreferredClientIP
))) 
  91                   memcpy(&PreferredClientIP
, &uip_all_zeroes_addr
, sizeof(uip_ipaddr_t
));        
  93                 switch (DHCPMessageType
) 
  96                                 /* If no preference was made or the preferred IP is already taken, find a new address */ 
  97                                 if (DHCPServerApp_CheckIfIPLeased(&PreferredClientIP
)) 
  98                                   DHCPServerApp_GetUnleasedIP(&PreferredClientIP
); 
 100                                 /* Create a new DHCP OFFER packet with the offered IP address */ 
 101                                 AppDataSize 
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_OFFER
, &RemoteMACAddress
, &PreferredClientIP
, TransactionID
); 
 103                                 /* Add network mask and router information to the list of DHCP OFFER packet options */ 
 104                                 AppDataSize 
+= DHCPCommon_SetOption(AppData
->Options
, DHCP_OPTION_SUBNET_MASK
, 
 105                                                                                                         sizeof(uip_ipaddr_t
), &Netmask
); 
 106                                 AppDataSize 
+= DHCPCommon_SetOption(AppData
->Options
, DHCP_OPTION_ROUTER
, 
 107                                                                         sizeof(uip_ipaddr_t
), &GatewayIPAddress
); 
 109                                 /* Send the DHCP OFFER packet */ 
 110                                 uip_poll_conn(BroadcastConnection
); 
 111                                 memcpy(&uip_udp_conn
->ripaddr
, &uip_broadcast_addr
, sizeof(uip_ipaddr_t
)); 
 112                                 uip_udp_send(AppDataSize
); 
 116                                 /* Check to see if the requested IP address has already been leased to a client */ 
 117                                 if (!(DHCPServerApp_CheckIfIPLeased(&PreferredClientIP
))) 
 119                                         /* Create a new DHCP ACK packet to accept the IP address lease */                                        
 120                                         AppDataSize 
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_ACK
, &RemoteMACAddress
, &PreferredClientIP
, TransactionID
); 
 122                                         /* Add network mask and router information to the list of DHCP ACK packet options */ 
 123                                         AppDataSize 
+= DHCPCommon_SetOption(AppData
->Options
, DHCP_OPTION_SUBNET_MASK
, 
 124                                                                                                                 sizeof(uip_ipaddr_t
), &Netmask
); 
 125                                         AppDataSize 
+= DHCPCommon_SetOption(AppData
->Options
, DHCP_OPTION_ROUTER
, 
 126                                                                             sizeof(uip_ipaddr_t
), &GatewayIPAddress
); 
 128                                         /* Mark the requested IP as leased to a client */ 
 129                                         DHCPServerApp_LeaseIP(&PreferredClientIP
); 
 133                                         /* Create a new DHCP NAK packet to reject the requested allocation */ 
 134                                         AppDataSize 
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_NAK
, &RemoteMACAddress
, &uip_all_zeroes_addr
, TransactionID
); 
 137                                 /* Send the DHCP ACK or NAK packet */ 
 138                                 uip_poll_conn(BroadcastConnection
); 
 139                                 memcpy(&uip_udp_conn
->ripaddr
, &uip_broadcast_addr
, sizeof(uip_ipaddr_t
)); 
 140                                 uip_udp_send(AppDataSize
); 
 144                                 /* Mark the IP address as released in the allocation table */ 
 145                                 DHCPServerApp_UnleaseIP(&uip_udp_conn
->ripaddr
); 
 151 /** Fills the DHCP packet response with the appropriate BOOTP header for DHCP. This fills out all the required 
 152  *  fields, leaving only the additional DHCP options to be added to the packet before it is sent to the DHCP client. 
 154  *  \param[out] DHCPHeader             Location in the packet buffer where the BOOTP header should be written to 
 155  *  \param[in]  DHCPMessageType        DHCP Message type, such as DHCP_DISCOVER 
 156  *  \param[in]  ClientHardwareAddress  Client MAC address the created transaction should be directed to 
 157  *  \param[in]  PreferredClientIP      Preferred IP that should be given to the client if it is unallocated 
 158  *  \param[in]  TransactionID          Transaction ID the created transaction should be associated with 
 160  *  \return Size in bytes of the created DHCP packet 
 162 static uint16_t DHCPServerApp_FillDHCPHeader(DHCP_Header_t
* const DHCPHeader
, 
 163                                              const uint8_t DHCPMessageType
, 
 164                                              const struct uip_eth_addr
* const ClientHardwareAddress
, 
 165                                                                                          const uip_ipaddr_t
* const PreferredClientIP
, 
 166                                              const uint32_t TransactionID
) 
 168         /* Erase existing packet data so that we start will all 0x00 DHCP header data */ 
 169         memset(DHCPHeader
, 0, sizeof(DHCP_Header_t
)); 
 171         DHCPHeader
->Operation             
= DHCPMessageType
; 
 172         DHCPHeader
->HardwareType          
= DHCP_HTYPE_ETHERNET
; 
 173         DHCPHeader
->HardwareAddressLength 
= sizeof(MACAddress
); 
 174         DHCPHeader
->Hops                  
= 0; 
 175         DHCPHeader
->TransactionID         
= TransactionID
; 
 176         DHCPHeader
->ElapsedSeconds        
= 0; 
 177         DHCPHeader
->Flags                 
= 0; 
 178         memcpy(&DHCPHeader
->NextServerIP
, &uip_hostaddr
, sizeof(uip_ipaddr_t
)); 
 179         memcpy(&DHCPHeader
->YourIP
, PreferredClientIP
, sizeof(uip_ipaddr_t
)); 
 180         memcpy(&DHCPHeader
->ClientHardwareAddress
, ClientHardwareAddress
, sizeof(struct uip_eth_addr
)); 
 181         DHCPHeader
->Cookie                
= DHCP_MAGIC_COOKIE
; 
 183         /* Add a DHCP message type and terminator options to the start of the DHCP options field */ 
 184         DHCPHeader
->Options
[0]            = DHCP_OPTION_MSG_TYPE
; 
 185         DHCPHeader
->Options
[1]            = 1; 
 186         DHCPHeader
->Options
[2]            = DHCPMessageType
; 
 187         DHCPHeader
->Options
[3]            = DHCP_OPTION_END
; 
 189         /* Calculate the total number of bytes added to the outgoing packet */ 
 190         return (sizeof(DHCP_Header_t
) + 4); 
 193 /** Checks to see if the nominated IP address has already been allocated to a client. 
 195  *  \param[in] IPAddress  IP Address whose lease status should be checked 
 197  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. 
 199  *  \return Boolean true if the IP has already been leased to a client, false otherwise. 
 201 static bool DHCPServerApp_CheckIfIPLeased(const uip_ipaddr_t
* const IPAddress
) 
 203         uint8_t Byte 
= (IPAddress
->u8
[3] / 8); 
 204         uint8_t Mask 
= (1 << (IPAddress
->u8
[3] % 8)); 
 206         /* Make sure that the requested IP address isn't already leased to the virtual server or another client */ 
 207         if (IPAddress
->u8
[3] && !(IPAddress
->u8
[3] == uip_hostaddr
.u8
[3]) && !(LeasedIPs
[Byte
] & Mask
)) 
 213 /** Retrieves the next unleased IP in the IP address pool. 
 215  *  \param[out] NewIPAddress  Location where the generated IP Address should be stored 
 217 static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t
* const NewIPAddress
) 
 219         uip_ipaddr_copy(NewIPAddress
, &uip_hostaddr
); 
 221         /** Look through the current subnet, skipping the broadcast and zero IP addresses */ 
 222         for (uint8_t IP 
= 1; IP 
< 254; IP
++) 
 224                 /* Update new IP address to lease with the current IP address to test */ 
 225                 NewIPAddress
->u8
[3] = IP
; 
 227                 /* If we've found an unleased IP, abort with the updated IP stored for the called */ 
 228                 if (!(DHCPServerApp_CheckIfIPLeased(NewIPAddress
))) 
 233 /** Marks the given IP Address as leased in the address pool, so that it will not be 
 234  *  allocated to another client unless it is first released. 
 236  *  \param[in] IPAddress  IP Address to mark as leased 
 238  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. 
 240 static void DHCPServerApp_LeaseIP(const uip_ipaddr_t
* const IPAddress
) 
 242         uint8_t Byte 
= (IPAddress
->u8
[3] / 8); 
 243         uint8_t Mask 
= (1 << (IPAddress
->u8
[3] % 8)); 
 245         /* Mark the IP address as leased in the allocation table */ 
 246         LeasedIPs
[Byte
] |= Mask
; 
 249 /** Marks the given IP Address as not leased in the address pool, so that it can be 
 250  *  allocated to another client upon request. 
 252  *  \param[in] IPAddress  IP Address to mark as not leased 
 254  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. 
 256 static void DHCPServerApp_UnleaseIP(const uip_ipaddr_t
* const IPAddress
) 
 258         uint8_t Byte 
= (IPAddress
->u8
[3] / 8); 
 259         uint8_t Mask 
= (1 << (IPAddress
->u8
[3] % 8)); 
 261         /* Mark the IP address as unleased in the allocation table */ 
 262         LeasedIPs
[Byte
] &= ~Mask
;