DHCP_Header_t* const AppData = (DHCP_Header_t*)uip_appdata;\r
uint16_t AppDataSize = 0;\r
\r
- uint8_t DHCPMessageType;\r
- if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &DHCPMessageType)))\r
- return;\r
-\r
- uip_ipaddr_t Netmask, GatewayIPAddress;\r
- struct uip_eth_addr RemoteMACAddress;\r
- uint32_t TransactionID;\r
+ /* Only process when new data arrives - don't retransmit lost packets */\r
+ if (uip_newdata())\r
+ {\r
+ /* Get the DHCP message type (if present), otherwise early-abort */\r
+ uint8_t DHCPMessageType;\r
+ if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &DHCPMessageType)))\r
+ return;\r
\r
- memcpy(&RemoteMACAddress, &AppData->ClientHardwareAddress, sizeof(struct uip_eth_addr));\r
- uip_getnetmask(&Netmask);\r
- uip_getdraddr(&GatewayIPAddress); \r
- TransactionID = AppData->TransactionID;\r
+ uip_ipaddr_t Netmask, GatewayIPAddress, PreferredClientIP;\r
+ struct uip_eth_addr RemoteMACAddress;\r
+ uint32_t TransactionID;\r
\r
- switch (DHCPMessageType)\r
- {\r
- case DHCP_DISCOVER:\r
- AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_OFFER, &RemoteMACAddress, TransactionID);\r
+ /* Get configured network mask, gateway IP and extract out DHCP transaction ID and remote IP */\r
+ uip_getnetmask(&Netmask);\r
+ uip_getdraddr(&GatewayIPAddress);\r
+ memcpy(&RemoteMACAddress, &AppData->ClientHardwareAddress, sizeof(struct uip_eth_addr));\r
+ TransactionID = AppData->TransactionID;\r
\r
- AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK,\r
- sizeof(uip_ipaddr_t), &Netmask);\r
- AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER,\r
- sizeof(uip_ipaddr_t), &GatewayIPAddress);\r
- \r
- /* Send the DHCP OFFER packet */\r
- uip_poll_conn(BroadcastConnection);\r
- memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));\r
- uip_udp_send(AppDataSize);\r
+ /* Try to extract out the client's preferred IP address if it is indicated in the packet */\r
+ if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_REQ_IPADDR, &PreferredClientIP)))\r
+ memcpy(&PreferredClientIP, &uip_all_zeroes_addr, sizeof(uip_ipaddr_t)); \r
+ \r
+ switch (DHCPMessageType)\r
+ {\r
+ case DHCP_DISCOVER:\r
+ /* If no preference was made or the preferred IP is already taken, find a new address */\r
+ if (DHCPServerApp_CheckIfIPLeased(&PreferredClientIP))\r
+ DHCPServerApp_GetUnleasedIP(&PreferredClientIP);\r
\r
- break;\r
- case DHCP_REQUEST:\r
- if (!(DHCPServerApp_CheckIfIPLeased(&AppData->YourIP)))\r
- {\r
- AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_ACK, &RemoteMACAddress, TransactionID);\r
+ /* Create a new DHCP OFFER packet with the offered IP address */\r
+ AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_OFFER, &RemoteMACAddress, &PreferredClientIP, TransactionID);\r
\r
+ /* Add network mask and router information to the list of DHCP OFFER packet options */\r
AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK,\r
sizeof(uip_ipaddr_t), &Netmask);\r
AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER,\r
- sizeof(uip_ipaddr_t), &GatewayIPAddress);\r
-\r
- DHCPServerApp_LeaseIP(&AppData->YourIP);\r
- }\r
- else\r
- {\r
- AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_NAK, &RemoteMACAddress, TransactionID); \r
- }\r
+ sizeof(uip_ipaddr_t), &GatewayIPAddress);\r
+\r
+ /* Send the DHCP OFFER packet */\r
+ uip_poll_conn(BroadcastConnection);\r
+ memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));\r
+ uip_udp_send(AppDataSize);\r
+\r
+ break;\r
+ case DHCP_REQUEST:\r
+ /* Check to see if the requested IP address has already been leased to a client */\r
+ if (!(DHCPServerApp_CheckIfIPLeased(&PreferredClientIP)))\r
+ {\r
+ /* Create a new DHCP ACK packet to accept the IP address lease */ \r
+ AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_ACK, &RemoteMACAddress, &PreferredClientIP, TransactionID);\r
+\r
+ /* Add network mask and router information to the list of DHCP ACK packet options */\r
+ AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK,\r
+ sizeof(uip_ipaddr_t), &Netmask);\r
+ AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER,\r
+ sizeof(uip_ipaddr_t), &GatewayIPAddress);\r
+\r
+ /* Mark the requested IP as leased to a client */\r
+ DHCPServerApp_LeaseIP(&PreferredClientIP);\r
+ }\r
+ else\r
+ {\r
+ /* Create a new DHCP NAK packet to reject the requested allocation */\r
+ AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_NAK, &RemoteMACAddress, &uip_all_zeroes_addr, TransactionID);\r
+ }\r
+ \r
+ /* Send the DHCP ACK or NAK packet */\r
+ uip_poll_conn(BroadcastConnection);\r
+ memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));\r
+ uip_udp_send(AppDataSize);\r
\r
- /* Send the DHCP ACK or NAK packet */\r
- uip_poll_conn(BroadcastConnection);\r
- memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));\r
- uip_udp_send(AppDataSize);\r
- \r
- break;\r
- case DHCP_RELEASE:\r
- /* Mark the IP address as released in the allocation table */\r
- DHCPServerApp_UnleaseIP(&uip_udp_conn->ripaddr);\r
- break;\r
+ break;\r
+ case DHCP_RELEASE:\r
+ /* Mark the IP address as released in the allocation table */\r
+ DHCPServerApp_UnleaseIP(&uip_udp_conn->ripaddr);\r
+ break;\r
+ }\r
}\r
}\r
\r
* \param[out] DHCPHeader Location in the packet buffer where the BOOTP header should be written to\r
* \param[in] DHCPMessageType DHCP Message type, such as DHCP_DISCOVER\r
* \param[in] ClientHardwareAddress Client MAC address the created transaction should be directed to\r
+ * \param[in] PreferredClientIP Preferred IP that should be given to the client if it is unallocated\r
* \param[in] TransactionID Transaction ID the created transaction should be associated with\r
*\r
* \return Size in bytes of the created DHCP packet\r
*/\r
static uint16_t DHCPServerApp_FillDHCPHeader(DHCP_Header_t* const DHCPHeader,\r
const uint8_t DHCPMessageType,\r
- struct uip_eth_addr* ClientHardwareAddress,\r
- uint32_t TransactionID)\r
+ const struct uip_eth_addr* const ClientHardwareAddress,\r
+ const uip_ipaddr_t* const PreferredClientIP,\r
+ const uint32_t TransactionID)\r
{\r
/* Erase existing packet data so that we start will all 0x00 DHCP header data */\r
memset(DHCPHeader, 0, sizeof(DHCP_Header_t));\r
DHCPHeader->ElapsedSeconds = 0;\r
DHCPHeader->Flags = 0;\r
memcpy(&DHCPHeader->NextServerIP, &uip_hostaddr, sizeof(uip_ipaddr_t));\r
- if (uip_ipaddr_cmp(&DHCPHeader->YourIP, &uip_all_zeroes_addr))\r
- DHCPServerApp_GetUnleasedIP(&DHCPHeader->YourIP);\r
+ memcpy(&DHCPHeader->YourIP, PreferredClientIP, sizeof(uip_ipaddr_t));\r
memcpy(&DHCPHeader->ClientHardwareAddress, ClientHardwareAddress, sizeof(struct uip_eth_addr));\r
DHCPHeader->Cookie = DHCP_MAGIC_COOKIE;\r
\r
*\r
* \return Boolean true if the IP has already been leased to a client, false otherwise.\r
*/\r
-static bool DHCPServerApp_CheckIfIPLeased(uip_ipaddr_t* IPAddress)\r
+static bool DHCPServerApp_CheckIfIPLeased(const uip_ipaddr_t* const IPAddress)\r
{\r
uint8_t Byte = (IPAddress->u8[3] / 8);\r
uint8_t Mask = (1 << (IPAddress->u8[3] % 8));\r
\r
- if (!(IPAddress->u8[3] == uip_hostaddr.u8[3]) && !(LeasedIPs[Byte] & Mask))\r
+ /* Make sure that the requested IP address isn't already leased to the virtual server or another client */\r
+ if (IPAddress->u8[3] && !(IPAddress->u8[3] == uip_hostaddr.u8[3]) && !(LeasedIPs[Byte] & Mask))\r
return false;\r
else\r
return true;\r
*\r
* \param[out] NewIPAddress Location where the generated IP Address should be stored\r
*/\r
-static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t* NewIPAddress)\r
+static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t* const NewIPAddress)\r
{\r
uip_ipaddr_copy(NewIPAddress, &uip_hostaddr);\r
\r
+ /** Look through the current subnet, skipping the broadcast and zero IP addresses */\r
for (uint8_t IP = 1; IP < 254; IP++)\r
{\r
+ /* Update new IP address to lease with the current IP address to test */\r
NewIPAddress->u8[3] = IP;\r
\r
+ /* If we've found an unleased IP, abort with the updated IP stored for the called */\r
if (!(DHCPServerApp_CheckIfIPLeased(NewIPAddress)))\r
return;\r
}\r
*\r
* \pre The IP address must be within the same /24 subnet as the virtual webserver.\r
*/\r
-static void DHCPServerApp_LeaseIP(uip_ipaddr_t* IPAddress)\r
+static void DHCPServerApp_LeaseIP(const uip_ipaddr_t* const IPAddress)\r
{\r
uint8_t Byte = (IPAddress->u8[3] / 8);\r
uint8_t Mask = (1 << (IPAddress->u8[3] % 8));\r
\r
+ /* Mark the IP address as leased in the allocation table */\r
LeasedIPs[Byte] |= Mask;\r
}\r
\r
*\r
* \pre The IP address must be within the same /24 subnet as the virtual webserver.\r
*/\r
-static void DHCPServerApp_UnleaseIP(uip_ipaddr_t* IPAddress)\r
+static void DHCPServerApp_UnleaseIP(const uip_ipaddr_t* const IPAddress)\r
{\r
uint8_t Byte = (IPAddress->u8[3] / 8);\r
uint8_t Mask = (1 << (IPAddress->u8[3] % 8));\r
\r
+ /* Mark the IP address as unleased in the allocation table */\r
LeasedIPs[Byte] &= ~Mask;\r
}\r
#endif\r