From a960e4b3b2ae72c6c3573fb690a1b9cde1642bc0 Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Wed, 27 Jan 2010 13:15:49 +0000 Subject: Add DHCP server to the Webserver demo for automatic network configuration. Correct uIP timer clock not tracking the correct timespan. --- Projects/Webserver/Lib/DHCPApp.c | 237 ++++++++++++++++++++++++++ Projects/Webserver/Lib/DHCPApp.h | 125 ++++++++++++++ Projects/Webserver/Lib/WebserverApp.c | 29 ++-- Projects/Webserver/Lib/WebserverApp.h | 11 ++ Projects/Webserver/Lib/uip/conf/apps-conf.h | 23 ++- Projects/Webserver/Lib/uip/conf/clock-arch.c | 3 +- Projects/Webserver/Lib/uip/conf/global-conf.h | 4 - Projects/Webserver/Lib/uip/conf/uip-conf.h | 6 +- 8 files changed, 410 insertions(+), 28 deletions(-) create mode 100644 Projects/Webserver/Lib/DHCPApp.c create mode 100644 Projects/Webserver/Lib/DHCPApp.h (limited to 'Projects/Webserver/Lib') diff --git a/Projects/Webserver/Lib/DHCPApp.c b/Projects/Webserver/Lib/DHCPApp.c new file mode 100644 index 000000000..ec5a815ef --- /dev/null +++ b/Projects/Webserver/Lib/DHCPApp.c @@ -0,0 +1,237 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * DHCP Client Application. When connected to the uIP stack, this will retrieve IP configuration settings from the + * DHCP server on the network. + */ + +#include "DHCPApp.h" + +#if defined(ENABLE_DHCP) +/** Timer for managing the timeout period for a DHCP server to respond */ +struct timer DHCPTimer; + +/** Initialization function for the DHCP client. */ +void DHCPApp_Init(void) +{ + uip_udp_appstate_t* const AppState = &uip_udp_conn->appstate; + + /* Create a new UDP connection to the DHCP server port for the DHCP solicitation */ + uip_ipaddr_t DHCPServerIPAddress; + uip_ipaddr(&DHCPServerIPAddress, 255, 255, 255, 255); + AppState->Connection = uip_udp_new(&DHCPServerIPAddress, HTONS(DHCPC_SERVER_PORT)); + + /* If the connection was sucessfully created, bind it to the local DHCP client port */ + if(AppState->Connection != NULL) + { + uip_udp_bind(AppState->Connection, HTONS(DHCPC_CLIENT_PORT)); + AppState->CurrentState = DHCP_STATE_SendDiscover; + } + + /* Set timeout period to half a second for a DHCP server to respond */ + timer_set(&DHCPTimer, CLOCK_SECOND / 2); +} + +/** uIP stack application callback for the DHCP client. This function must be called each time the TCP/IP stack + * needs a UDP packet to be processed. + */ +void DHCPApp_Callback(void) +{ + uip_udp_appstate_t* const AppState = &uip_udp_conn->appstate; + DHCP_Header_t* const AppData = (DHCP_Header_t*)uip_appdata; + uint16_t AppDataSize = 0; + + switch (AppState->CurrentState) + { + case DHCP_STATE_SendDiscover: + /* Clear all DHCP settings, reset client IP address */ + memset(&AppState->DHCPOffer_Data, 0x00, sizeof(AppState->DHCPOffer_Data)); + uip_sethostaddr(&AppState->DHCPOffer_Data.AllocatedIP); + + /* Fill out the DHCP response header */ + AppDataSize += DHCPApp_FillDHCPHeader(AppData, DHCP_DISCOVER, AppState); + + /* Add the required DHCP options list to the packet */ + uint8_t RequiredOptionList[] = {DHCP_OPTION_SUBNET_MASK, DHCP_OPTION_ROUTER, DHCP_OPTION_DNS_SERVER}; + AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_REQ_LIST, sizeof(RequiredOptionList), + RequiredOptionList); + + /* Send the DHCP DISCOVER packet */ + uip_send(AppData, AppDataSize); + + /* Reset the timeout timer, progress to next state */ + timer_reset(&DHCPTimer); + AppState->CurrentState = DHCP_STATE_WaitForResponse; + + break; + case DHCP_STATE_WaitForResponse: + if (!(uip_newdata())) + { + /* Check if the DHCP timeout period has expired while waiting for a response */ + if (timer_expired(&DHCPTimer)) + AppState->CurrentState = DHCP_STATE_SendDiscover; + + break; + } + + uint8_t OfferResponse_MessageType; + if ((AppData->TransactionID == DHCP_TRANSACTION_ID) && + DHCPApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &OfferResponse_MessageType) && + (OfferResponse_MessageType == DHCP_OFFER)) + { + /* Received a DHCP offer for an IP address, copy over values for later request */ + memcpy(&AppState->DHCPOffer_Data.AllocatedIP, &AppData->YourIP, sizeof(uip_ipaddr_t)); + DHCPApp_GetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK, &AppState->DHCPOffer_Data.Netmask); + DHCPApp_GetOption(AppData->Options, DHCP_OPTION_ROUTER, &AppState->DHCPOffer_Data.GatewayIP); + DHCPApp_GetOption(AppData->Options, DHCP_OPTION_SERVER_ID, &AppState->DHCPOffer_Data.ServerIP); + + timer_reset(&DHCPTimer); + AppState->CurrentState = DHCP_STATE_SendRequest; + } + + break; + case DHCP_STATE_SendRequest: + /* Fill out the DHCP response header */ + AppDataSize += DHCPApp_FillDHCPHeader(AppData, DHCP_REQUEST, AppState); + + /* Add the DHCP REQUESTED IP ADDRESS option to the packet */ + AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_REQ_IPADDR, sizeof(uip_ipaddr_t), + &AppState->DHCPOffer_Data.AllocatedIP); + + /* Add the DHCP SERVER IP ADDRESS option to the packet */ + AppDataSize += DHCPApp_SetOption(AppData->Options, DHCP_OPTION_SERVER_ID, sizeof(uip_ipaddr_t), + &AppState->DHCPOffer_Data.ServerIP); + + /* Send the DHCP REQUEST packet */ + uip_send(AppData, AppDataSize); + + /* Reset the timeout timer, progress to next state */ + timer_reset(&DHCPTimer); + AppState->CurrentState = DHCP_STATE_WaitForACK; + + break; + case DHCP_STATE_WaitForACK: + if (!(uip_newdata())) + { + /* Check if the DHCP timeout period has expired while waiting for a response */ + if (timer_expired(&DHCPTimer)) + AppState->CurrentState = DHCP_STATE_SendDiscover; + + break; + } + + uint8_t RequestResponse_MessageType; + if ((AppData->TransactionID == DHCP_TRANSACTION_ID) && + DHCPApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &RequestResponse_MessageType) && + (RequestResponse_MessageType == DHCP_ACK)) + { + /* Set the new network parameters from the DHCP server */ + uip_sethostaddr(&AppState->DHCPOffer_Data.AllocatedIP); + uip_setnetmask(&AppState->DHCPOffer_Data.Netmask); + uip_setdraddr(&AppState->DHCPOffer_Data.GatewayIP); + + AppState->CurrentState = DHCP_STATE_AddressLeased; + } + + break; + } +} + +uint16_t DHCPApp_FillDHCPHeader(DHCP_Header_t* DHCPHeader, uint8_t DHCPMessageType, uip_udp_appstate_t* AppState) +{ + /* Erase existing packet data so that we start will all 0x00 DHCP header data */ + memset(DHCPHeader, 0, sizeof(DHCP_Header_t)); + + /* Fill out the DHCP packet header */ + DHCPHeader->Operation = DHCP_OP_BOOTREQUEST; + DHCPHeader->HardwareType = DHCP_HTYPE_ETHERNET; + DHCPHeader->HardwareAddressLength = sizeof(MACAddress); + DHCPHeader->Hops = 0; + DHCPHeader->TransactionID = DHCP_TRANSACTION_ID; + DHCPHeader->ElapsedSeconds = 0; + DHCPHeader->Flags = HTONS(BOOTP_BROADCAST); + memcpy(&DHCPHeader->ClientIP, &uip_hostaddr, sizeof(uip_ipaddr_t)); + memcpy(&DHCPHeader->YourIP, &AppState->DHCPOffer_Data.AllocatedIP, sizeof(uip_ipaddr_t)); + memcpy(&DHCPHeader->NextServerIP, &AppState->DHCPOffer_Data.ServerIP, sizeof(uip_ipaddr_t)); + memcpy(&DHCPHeader->ClientHardwareAddress, &MACAddress, sizeof(struct uip_eth_addr)); + DHCPHeader->Cookie = DHCP_MAGIC_COOKIE; + + /* Add a DHCP type and terminator options to the start of the DHCP options field */ + DHCPHeader->Options[0] = DHCP_OPTION_MSG_TYPE; + DHCPHeader->Options[1] = 1; + DHCPHeader->Options[2] = DHCPMessageType; + DHCPHeader->Options[3] = DHCP_OPTION_END; + + /* Calculate the total number of bytes added to the outgoing packet */ + return (sizeof(DHCP_Header_t) + 4); +} + +uint8_t DHCPApp_SetOption(uint8_t* DHCPOptionList, uint8_t Option, uint8_t DataLen, void* Source) +{ + /* Skip through the DHCP options list until the terminator option is found */ + while (*DHCPOptionList != DHCP_OPTION_END) + DHCPOptionList += (DHCPOptionList[1] + 2); + + /* Overwrite the existing terminator with the new option, add a new terminator at the end of the list */ + DHCPOptionList[0] = Option; + DHCPOptionList[1] = DataLen; + memcpy(&DHCPOptionList[2], Source, DataLen); + DHCPOptionList[2 + DataLen] = DHCP_OPTION_END; + + /* Calculate the total number of bytes added to the outgoing packet */ + return (2 + DataLen); +} + +bool DHCPApp_GetOption(uint8_t* DHCPOptionList, uint8_t Option, void* Destination) +{ + /* Look through the incomming DHCP packet's options list for the requested option */ + while (*DHCPOptionList != DHCP_OPTION_END) + { + /* Check if the current DHCP option in the packet is the one requested */ + if (DHCPOptionList[0] == Option) + { + /* Copy request option's data to the destination buffer */ + memcpy(Destination, &DHCPOptionList[2], DHCPOptionList[1]); + + /* Indicate that the requested option data was sucessfully retrieved */ + return true; + } + + /* Skip to next DHCP option in the options list */ + DHCPOptionList += (DHCPOptionList[1] + 2); + } + + /* Requested option not found in the incomming packet's DHCP options list */ + return false; +} + +#endif diff --git a/Projects/Webserver/Lib/DHCPApp.h b/Projects/Webserver/Lib/DHCPApp.h new file mode 100644 index 000000000..9e9faaa36 --- /dev/null +++ b/Projects/Webserver/Lib/DHCPApp.h @@ -0,0 +1,125 @@ +/* + LUFA Library + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +/* + Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that the copyright notice and this + permission notice and warranty disclaimer appear in supporting + documentation, and that the name of the author not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + The author disclaim all warranties with regard to this + software, including all implied warranties of merchantability + and fitness. In no event shall the author be liable for any + special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether + in an action of contract, negligence or other tortious action, + arising out of or in connection with the use or performance of + this software. +*/ + +/** \file + * + * Header file for DHCPApp.c. + */ + +#ifndef _DHCP_APP_H_ +#define _DHCP_APP_H_ + + /* Includes: */ + #include + + #include + + #include "../Webserver.h" + + /* Macros: */ + #define DHCPC_SERVER_PORT 67 + #define DHCPC_CLIENT_PORT 68 + + #define DHCP_OP_BOOTREQUEST 0x01 + #define DHCP_OP_BOOTREPLY 0x02 + + #define BOOTP_BROADCAST 0x8000 + + #define DHCP_MAGIC_COOKIE 0x63538263 + + #define DHCP_TRANSACTION_ID 0x13245466 + + #define DHCP_DISCOVER 1 + #define DHCP_OFFER 2 + #define DHCP_REQUEST 3 + #define DHCP_DECLINE 4 + #define DHCP_ACK 5 + #define DHCP_NAK 6 + #define DHCP_RELEASE 7 + + #define DHCP_HTYPE_ETHERNET 1 + + #define DHCP_OPTION_SUBNET_MASK 1 + #define DHCP_OPTION_ROUTER 3 + #define DHCP_OPTION_DNS_SERVER 6 + #define DHCP_OPTION_REQ_IPADDR 50 + #define DHCP_OPTION_LEASE_TIME 51 + #define DHCP_OPTION_MSG_TYPE 53 + #define DHCP_OPTION_SERVER_ID 54 + #define DHCP_OPTION_REQ_LIST 55 + #define DHCP_OPTION_END 255 + + /* Type Defines: */ + /** Type define for a DHCP packet inside an Ethernet frame. */ + typedef struct + { + uint8_t Operation; /**< DHCP operation, either DHCP_OP_BOOTREQUEST or DHCP_OP_BOOTREPLY */ + uint8_t HardwareType; /**< Hardware carrier type constant */ + uint8_t HardwareAddressLength; /**< Length in bytes of a hardware (MAC) address on the network */ + uint8_t Hops; /**< Number of hops required to reach the server, unused */ + + uint32_t TransactionID; /**< Unique ID of the DHCP packet, for positive matching between sent and received packets */ + + uint16_t ElapsedSeconds; /**< Elapsed seconds since the request was made */ + uint16_t Flags; /**< BOOTP packet flags */ + + uip_ipaddr_t ClientIP; /**< Client IP address, if already leased an IP */ + uip_ipaddr_t YourIP; /**< Client IP address */ + uip_ipaddr_t NextServerIP; /**< Legacy BOOTP protocol field, unused for DHCP */ + uip_ipaddr_t RelayAgentIP; /**< Legacy BOOTP protocol field, unused for DHCP */ + + uint8_t ClientHardwareAddress[16]; /**< Hardware (MAC) address of the client making a request to the DHCP server */ + uint8_t ServerHostnameString[64]; /**< Legacy BOOTP protocol field, unused for DHCP */ + uint8_t BootFileName[128]; /**< Legacy BOOTP protocol field, unused for DHCP */ + + uint32_t Cookie; /**< Magic BOOTP protocol cookie to indicate a valid packet */ + + uint8_t Options[]; /** DHCP message options */ + } DHCP_Header_t; + + /* Enums: */ + enum DHCP_States_t + { + DHCP_STATE_SendDiscover, + DHCP_STATE_WaitForResponse, + DHCP_STATE_SendRequest, + DHCP_STATE_WaitForACK, + DHCP_STATE_AddressLeased, + }; + + /* Function Prototypes: */ + void DHCPApp_Init(void); + void DHCPApp_Callback(void); + + uint16_t DHCPApp_FillDHCPHeader(DHCP_Header_t* DHCPHeader, uint8_t DHCPMessageType, uip_udp_appstate_t* AppState); + uint8_t DHCPApp_SetOption(uint8_t* DHCPOptionList, uint8_t Option, uint8_t DataLen, void* Source); + bool DHCPApp_GetOption(uint8_t* DHCPOptionList, uint8_t Option, void* Destination); + +#endif diff --git a/Projects/Webserver/Lib/WebserverApp.c b/Projects/Webserver/Lib/WebserverApp.c index cba3b43fa..02f38be87 100644 --- a/Projects/Webserver/Lib/WebserverApp.c +++ b/Projects/Webserver/Lib/WebserverApp.c @@ -76,7 +76,7 @@ char PROGMEM HTTPPage[] = void WebserverApp_Init(void) { /* Listen on port 80 for HTTP connections from hosts */ - uip_listen(HTONS(80)); + uip_listen(HTONS(HTTP_SERVER_PORT)); } /** uIP stack application callback for the simple HTTP webserver. This function must be called each time the @@ -84,8 +84,9 @@ void WebserverApp_Init(void) */ void WebserverApp_Callback(void) { - char* AppDataPtr = (char*)uip_appdata; - uint16_t AppDataSize = 0; + uip_tcp_appstate_t* const AppState = &uip_conn->appstate; + char* AppData = (char*)uip_appdata; + uint16_t AppDataSize = 0; if (uip_closed() || uip_aborted() || uip_timedout()) { @@ -95,12 +96,12 @@ void WebserverApp_Callback(void) else if (uip_connected()) { /* New connection - initialize connection state and data pointer to the appropriate HTTP header */ - uip_conn->appstate.SendPos = HTTP200Header; - uip_conn->appstate.CurrentState = WEBSERVER_STATE_SendHeaders; + AppState->SendPos = HTTP200Header; + AppState->CurrentState = WEBSERVER_STATE_SendHeaders; } /* Calculate the maximum segment size and remaining data size */ - uint16_t BytesRemaining = strlen_P(uip_conn->appstate.SendPos); + uint16_t BytesRemaining = strlen_P(AppState->SendPos); uint16_t MaxSegSize = uip_mss(); /* No more bytes remaining in the current data being sent - progress to next data chunk or @@ -108,15 +109,15 @@ void WebserverApp_Callback(void) if (!(BytesRemaining)) { /* Check which data chunk we are currently sending (header or data) */ - if (uip_conn->appstate.CurrentState == WEBSERVER_STATE_SendHeaders) + if (AppState->CurrentState == WEBSERVER_STATE_SendHeaders) { - uip_conn->appstate.SendPos = HTTPPage; - uip_conn->appstate.CurrentState = WEBSERVER_STATE_SendData; + AppState->SendPos = HTTPPage; + AppState->CurrentState = WEBSERVER_STATE_SendData; } - else if (uip_conn->appstate.CurrentState == WEBSERVER_STATE_SendData) + else if (AppState->CurrentState == WEBSERVER_STATE_SendData) { uip_close(); - uip_conn->appstate.CurrentState = WEBSERVER_STATE_Closed; + AppState->CurrentState = WEBSERVER_STATE_Closed; } return; @@ -133,9 +134,9 @@ void WebserverApp_Callback(void) } /* Copy over the next data segment to the application buffer, advance send position pointer */ - strncpy_P(uip_appdata, uip_conn->appstate.SendPos, AppDataSize); - uip_conn->appstate.SendPos += AppDataSize; + strncpy_P(AppData, AppState->SendPos, AppDataSize); + AppState->SendPos += AppDataSize; /* Send the data to the requesting host */ - uip_send(AppDataPtr, AppDataSize); + uip_send(AppData, AppDataSize); } diff --git a/Projects/Webserver/Lib/WebserverApp.h b/Projects/Webserver/Lib/WebserverApp.h index 4d1d76cc9..3cb97c667 100644 --- a/Projects/Webserver/Lib/WebserverApp.h +++ b/Projects/Webserver/Lib/WebserverApp.h @@ -43,6 +43,17 @@ #include #include + + /* Enums: */ + enum Webserver_States_t + { + WEBSERVER_STATE_SendHeaders, + WEBSERVER_STATE_SendData, + WEBSERVER_STATE_Closed, + }; + + /* Macros: */ + #define HTTP_SERVER_PORT 80 /* Function Prototypes: */ void WebserverApp_Init(void); diff --git a/Projects/Webserver/Lib/uip/conf/apps-conf.h b/Projects/Webserver/Lib/uip/conf/apps-conf.h index 22bab81bf..fc9727dcd 100644 --- a/Projects/Webserver/Lib/uip/conf/apps-conf.h +++ b/Projects/Webserver/Lib/uip/conf/apps-conf.h @@ -1,20 +1,29 @@ #ifndef __APPS_CONF_H__ #define __APPS_CONF_H__ - enum Webserver_States_t - { - WEBSERVER_STATE_SendHeaders, - WEBSERVER_STATE_SendData, - WEBSERVER_STATE_Closed, - }; - typedef struct { uint8_t CurrentState; char* SendPos; } uip_tcp_appstate_t; + typedef struct + { + uint8_t CurrentState; + struct uip_udp_conn* Connection; + + struct + { + uint8_t AllocatedIP[4]; + uint8_t Netmask[4]; + uint8_t GatewayIP[4]; + uint8_t ServerIP[4]; + } DHCPOffer_Data; + } uip_udp_appstate_t; + #define UIP_APPCALL WebserverApp_Callback + #define UIP_UDP_APPCALL DHCPApp_Callback void UIP_APPCALL(void); + void UIP_UDP_APPCALL(void); #endif /*__APPS_CONF_H__*/ diff --git a/Projects/Webserver/Lib/uip/conf/clock-arch.c b/Projects/Webserver/Lib/uip/conf/clock-arch.c index 8363d96e0..2007de4e9 100644 --- a/Projects/Webserver/Lib/uip/conf/clock-arch.c +++ b/Projects/Webserver/Lib/uip/conf/clock-arch.c @@ -21,8 +21,7 @@ ISR(TIMER1_COMPA_vect) void clock_init() { OCR1A = ((F_CPU / 1024) / 100); - TCCR1A = (1 << WGM12); - TCCR1B = ((1 << CS12) | (1 << CS10)); + TCCR1B = ((1 << WGM12) | (1 << CS12) | (1 << CS10)); TIMSK1 = (1 << OCIE1A); } diff --git a/Projects/Webserver/Lib/uip/conf/global-conf.h b/Projects/Webserver/Lib/uip/conf/global-conf.h index a9f118d4d..5cfc60746 100644 --- a/Projects/Webserver/Lib/uip/conf/global-conf.h +++ b/Projects/Webserver/Lib/uip/conf/global-conf.h @@ -2,10 +2,6 @@ #ifndef __GLOBAL_CONF_H__ #define __GLOBAL_CONF_H__ -//Define frequency -// #define F_CPU 12500000UL -// - //Include uip.h gives all the uip configurations in uip-conf.h #include "uip.h" diff --git a/Projects/Webserver/Lib/uip/conf/uip-conf.h b/Projects/Webserver/Lib/uip/conf/uip-conf.h index d3d9bc453..e89e7c4c6 100644 --- a/Projects/Webserver/Lib/uip/conf/uip-conf.h +++ b/Projects/Webserver/Lib/uip/conf/uip-conf.h @@ -79,7 +79,11 @@ typedef unsigned short uip_stats_t; * * \hideinitializer */ -#define UIP_CONF_UDP 0 +#if defined(ENABLE_DHCP) + #define UIP_CONF_UDP 1 +#else + #define UIP_CONF_UDP 0 +#endif /** * UDP checksums on or off -- cgit v1.2.3