Add DHCP server to the Webserver demo for automatic network configuration. Correct...
[pub/lufa.git] / Projects / Webserver / Lib / DHCPApp.c
diff --git a/Projects/Webserver/Lib/DHCPApp.c b/Projects/Webserver/Lib/DHCPApp.c
new file mode 100644 (file)
index 0000000..ec5a815
--- /dev/null
@@ -0,0 +1,237 @@
+/*\r
+             LUFA Library\r
+     Copyright (C) Dean Camera, 2010.\r
+              \r
+  dean [at] fourwalledcubicle [dot] com\r
+      www.fourwalledcubicle.com\r
+*/\r
+\r
+/*\r
+  Copyright 2010  Dean Camera (dean [at] fourwalledcubicle [dot] com)\r
+\r
+  Permission to use, copy, modify, distribute, and sell this \r
+  software and its documentation for any purpose is hereby granted\r
+  without fee, provided that the above copyright notice appear in \r
+  all copies and that both that the copyright notice and this\r
+  permission notice and warranty disclaimer appear in supporting \r
+  documentation, and that the name of the author not be used in \r
+  advertising or publicity pertaining to distribution of the \r
+  software without specific, written prior permission.\r
+\r
+  The author disclaim all warranties with regard to this\r
+  software, including all implied warranties of merchantability\r
+  and fitness.  In no event shall the author be liable for any\r
+  special, indirect or consequential damages or any damages\r
+  whatsoever resulting from loss of use, data or profits, whether\r
+  in an action of contract, negligence or other tortious action,\r
+  arising out of or in connection with the use or performance of\r
+  this software.\r
+*/\r
+\r
+/** \file\r
+ *\r
+ *  DHCP Client Application. When connected to the uIP stack, this will retrieve IP configuration settings from the\r
+ *  DHCP server on the network.\r
+ */\r
\r
+#include "DHCPApp.h"\r
+\r
+#if defined(ENABLE_DHCP)\r
+/** Timer for managing the timeout period for a DHCP server to respond */\r
+struct timer DHCPTimer;\r
+\r
+/** Initialization function for the DHCP client. */\r
+void DHCPApp_Init(void)\r
+{\r
+       uip_udp_appstate_t* const AppState = &uip_udp_conn->appstate;\r
+       \r
+       /* Create a new UDP connection to the DHCP server port for the DHCP solicitation */\r
+       uip_ipaddr_t DHCPServerIPAddress;\r
+       uip_ipaddr(&DHCPServerIPAddress, 255, 255, 255, 255);\r
+       AppState->Connection = uip_udp_new(&DHCPServerIPAddress, HTONS(DHCPC_SERVER_PORT));\r
+       \r
+       /* If the connection was sucessfully created, bind it to the local DHCP client port */\r
+       if(AppState->Connection != NULL)\r
+       {\r
+               uip_udp_bind(AppState->Connection, HTONS(DHCPC_CLIENT_PORT));\r
+               AppState->CurrentState = DHCP_STATE_SendDiscover;\r
+       }\r
+\r
+       /* Set timeout period to half a second for a DHCP server to respond */\r
+       timer_set(&DHCPTimer, CLOCK_SECOND / 2);\r
+}\r
\r
+/** uIP stack application callback for the DHCP client. This function must be called each time the TCP/IP stack \r
+ *  needs a UDP packet to be processed.\r
+ */\r
+void DHCPApp_Callback(void)\r
+{\r
+       uip_udp_appstate_t* const AppState    = &uip_udp_conn->appstate;\r
+       DHCP_Header_t*      const AppData     = (DHCP_Header_t*)uip_appdata;\r
+       uint16_t                  AppDataSize = 0;\r
+       \r
+       switch (AppState->CurrentState)\r
+       {\r
+               case DHCP_STATE_SendDiscover:\r
+                       /* Clear all DHCP settings, reset client IP address */\r
+                       memset(&AppState->DHCPOffer_Data, 0x00, sizeof(AppState->DHCPOffer_Data));\r
+                       uip_sethostaddr(&AppState->DHCPOffer_Data.AllocatedIP);\r
+\r
+                       /* Fill out the DHCP response header */\r
+                       AppDataSize += DHCPApp_FillDHCPHeader(AppData, DHCP_DISCOVER, AppState);\r
+                       \r
+                       /* Add the required DHCP options list to the packet */\r
+                       uint8_t RequiredOptionList[] = {DHCP_OPTION_SUBNET_MASK, DHCP_OPTION_ROUTER, DHCP_OPTION_DNS_SERVER};\r
+                       AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_REQ_LIST, sizeof(RequiredOptionList),\r
+                                                        RequiredOptionList);                   \r
+                       \r
+                       /* Send the DHCP DISCOVER packet */\r
+                       uip_send(AppData, AppDataSize);\r
+\r
+                       /* Reset the timeout timer, progress to next state */\r
+                       timer_reset(&DHCPTimer);\r
+                       AppState->CurrentState = DHCP_STATE_WaitForResponse;                    \r
+                       \r
+                       break;\r
+               case DHCP_STATE_WaitForResponse:\r
+                       if (!(uip_newdata()))\r
+                       {\r
+                               /* Check if the DHCP timeout period has expired while waiting for a response */\r
+                               if (timer_expired(&DHCPTimer))\r
+                                 AppState->CurrentState = DHCP_STATE_SendDiscover;\r
+                               \r
+                               break;\r
+                       }\r
+                         \r
+                       uint8_t OfferResponse_MessageType;\r
+                       if ((AppData->TransactionID == DHCP_TRANSACTION_ID) &&\r
+                           DHCPApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &OfferResponse_MessageType) &&\r
+                           (OfferResponse_MessageType == DHCP_OFFER))\r
+                       {\r
+                               /* Received a DHCP offer for an IP address, copy over values for later request */\r
+                               memcpy(&AppState->DHCPOffer_Data.AllocatedIP, &AppData->YourIP, sizeof(uip_ipaddr_t));\r
+                               DHCPApp_GetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK, &AppState->DHCPOffer_Data.Netmask);\r
+                               DHCPApp_GetOption(AppData->Options, DHCP_OPTION_ROUTER,      &AppState->DHCPOffer_Data.GatewayIP);\r
+                               DHCPApp_GetOption(AppData->Options, DHCP_OPTION_SERVER_ID,   &AppState->DHCPOffer_Data.ServerIP);\r
+                               \r
+                               timer_reset(&DHCPTimer);\r
+                               AppState->CurrentState = DHCP_STATE_SendRequest;\r
+                       }\r
+\r
+                       break;\r
+               case DHCP_STATE_SendRequest:\r
+                       /* Fill out the DHCP response header */\r
+                       AppDataSize += DHCPApp_FillDHCPHeader(AppData, DHCP_REQUEST, AppState);\r
+\r
+                       /* Add the DHCP REQUESTED IP ADDRESS option to the packet */\r
+                       AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_REQ_IPADDR, sizeof(uip_ipaddr_t),\r
+                                                        &AppState->DHCPOffer_Data.AllocatedIP);\r
+\r
+                       /* Add the DHCP SERVER IP ADDRESS option to the packet */\r
+                       AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_SERVER_ID, sizeof(uip_ipaddr_t),\r
+                                                        &AppState->DHCPOffer_Data.ServerIP);\r
+\r
+                       /* Send the DHCP REQUEST packet */\r
+                       uip_send(AppData, AppDataSize);\r
+                       \r
+                       /* Reset the timeout timer, progress to next state */\r
+                       timer_reset(&DHCPTimer);\r
+                       AppState->CurrentState = DHCP_STATE_WaitForACK;\r
+\r
+                       break;\r
+               case DHCP_STATE_WaitForACK:\r
+                       if (!(uip_newdata()))\r
+                       {\r
+                               /* Check if the DHCP timeout period has expired while waiting for a response */\r
+                               if (timer_expired(&DHCPTimer))\r
+                                 AppState->CurrentState = DHCP_STATE_SendDiscover;\r
+                               \r
+                               break;\r
+                       }\r
+                       \r
+                       uint8_t RequestResponse_MessageType;\r
+                       if ((AppData->TransactionID == DHCP_TRANSACTION_ID) &&\r
+                           DHCPApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &RequestResponse_MessageType) &&\r
+                           (RequestResponse_MessageType == DHCP_ACK))\r
+                       {\r
+                               /* Set the new network parameters from the DHCP server */\r
+                               uip_sethostaddr(&AppState->DHCPOffer_Data.AllocatedIP);\r
+                               uip_setnetmask(&AppState->DHCPOffer_Data.Netmask);\r
+                               uip_setdraddr(&AppState->DHCPOffer_Data.GatewayIP);\r
+                       \r
+                               AppState->CurrentState = DHCP_STATE_AddressLeased;\r
+                       }\r
+                       \r
+                       break;\r
+       }\r
+}\r
+\r
+uint16_t DHCPApp_FillDHCPHeader(DHCP_Header_t* DHCPHeader, uint8_t DHCPMessageType, uip_udp_appstate_t* AppState)\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
+\r
+       /* Fill out the DHCP packet header */\r
+       DHCPHeader->Operation             = DHCP_OP_BOOTREQUEST;\r
+       DHCPHeader->HardwareType          = DHCP_HTYPE_ETHERNET;\r
+       DHCPHeader->HardwareAddressLength = sizeof(MACAddress);\r
+       DHCPHeader->Hops                  = 0;\r
+       DHCPHeader->TransactionID         = DHCP_TRANSACTION_ID;\r
+       DHCPHeader->ElapsedSeconds        = 0;\r
+       DHCPHeader->Flags                 = HTONS(BOOTP_BROADCAST);\r
+       memcpy(&DHCPHeader->ClientIP, &uip_hostaddr, sizeof(uip_ipaddr_t));\r
+       memcpy(&DHCPHeader->YourIP, &AppState->DHCPOffer_Data.AllocatedIP, sizeof(uip_ipaddr_t));\r
+       memcpy(&DHCPHeader->NextServerIP, &AppState->DHCPOffer_Data.ServerIP, sizeof(uip_ipaddr_t));\r
+       memcpy(&DHCPHeader->ClientHardwareAddress, &MACAddress, sizeof(struct uip_eth_addr));\r
+       DHCPHeader->Cookie                = DHCP_MAGIC_COOKIE;\r
+       \r
+       /* Add a DHCP type and terminator options to the start of the DHCP options field */\r
+       DHCPHeader->Options[0]            = DHCP_OPTION_MSG_TYPE;\r
+       DHCPHeader->Options[1]            = 1;\r
+       DHCPHeader->Options[2]            = DHCPMessageType;\r
+       DHCPHeader->Options[3]            = DHCP_OPTION_END;\r
+       \r
+       /* Calculate the total number of bytes added to the outgoing packet */\r
+       return (sizeof(DHCP_Header_t) + 4);\r
+}\r
+\r
+uint8_t DHCPApp_SetOption(uint8_t* DHCPOptionList, uint8_t Option, uint8_t DataLen, void* Source)\r
+{\r
+       /* Skip through the DHCP options list until the terminator option is found */\r
+       while (*DHCPOptionList != DHCP_OPTION_END)\r
+         DHCPOptionList += (DHCPOptionList[1] + 2);\r
+\r
+       /* Overwrite the existing terminator with the new option, add a new terminator at the end of the list */\r
+       DHCPOptionList[0] = Option;\r
+       DHCPOptionList[1] = DataLen;\r
+       memcpy(&DHCPOptionList[2], Source, DataLen);\r
+       DHCPOptionList[2 + DataLen] = DHCP_OPTION_END;\r
+       \r
+       /* Calculate the total number of bytes added to the outgoing packet */\r
+       return (2 + DataLen);\r
+}\r
+\r
+bool DHCPApp_GetOption(uint8_t* DHCPOptionList, uint8_t Option, void* Destination)\r
+{\r
+       /* Look through the incomming DHCP packet's options list for the requested option */\r
+       while (*DHCPOptionList != DHCP_OPTION_END)\r
+       {\r
+               /* Check if the current DHCP option in the packet is the one requested */\r
+               if (DHCPOptionList[0] == Option)\r
+               {\r
+                       /* Copy request option's data to the destination buffer */\r
+                       memcpy(Destination, &DHCPOptionList[2], DHCPOptionList[1]);\r
+                       \r
+                       /* Indicate that the requested option data was sucessfully retrieved */\r
+                       return true;\r
+               }\r
+               \r
+               /* Skip to next DHCP option in the options list */\r
+               DHCPOptionList += (DHCPOptionList[1] + 2);\r
+       }\r
+       \r
+       /* Requested option not found in the incomming packet's DHCP options list */\r
+       return false;\r
+}\r
+\r
+#endif\r