|  |  |  | /*
 | 
					
						
							|  |  |  |              LUFA Library | 
					
						
							|  |  |  |      Copyright (C) Dean Camera, 2016. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   dean [at] fourwalledcubicle [dot] com | 
					
						
							|  |  |  |            www.lufa-lib.org | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   Copyright 2016  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 disclaims 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 Server Application. When connected to the uIP stack, this will send IP configuration settings to a | 
					
						
							|  |  |  |  *  DHCP client on the network. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define  INCLUDE_FROM_DHCPSERVERAPP_C
 | 
					
						
							|  |  |  | #include "DHCPServerApp.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(ENABLE_DHCP_SERVER) || defined(__DOXYGEN__)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct uip_conn* BroadcastConnection; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint8_t LeasedIPs[255 / 8]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Initialization function for the DHCP server. */ | 
					
						
							|  |  |  | void DHCPServerApp_Init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Listen on port 67 for DHCP server connections from hosts */ | 
					
						
							|  |  |  | 	uip_listen(HTONS(DHCP_SERVER_PORT)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Create a new UDP connection to the DHCP server port for the DHCP solicitation */ | 
					
						
							|  |  |  | 	struct uip_udp_conn* BroadcastConnection = uip_udp_new(&uip_broadcast_addr, HTONS(DHCP_CLIENT_PORT)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If the connection was successfully created, bind it to the local DHCP client port */ | 
					
						
							|  |  |  | 	if (BroadcastConnection != NULL) | 
					
						
							|  |  |  | 	  uip_udp_bind(BroadcastConnection, HTONS(DHCP_SERVER_PORT)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set all IP addresses as unleased */ | 
					
						
							|  |  |  | 	memset(LeasedIPs, 0x00, sizeof(LeasedIPs)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** uIP stack application callback for the DHCP server. This function must be called each time the TCP/IP stack
 | 
					
						
							|  |  |  |  *  needs a UDP packet to be processed. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void DHCPServerApp_Callback(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	DHCP_Header_t* const AppData     = (DHCP_Header_t*)uip_appdata; | 
					
						
							|  |  |  | 	uint16_t             AppDataSize = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Only process when new data arrives - don't retransmit lost packets */ | 
					
						
							|  |  |  | 	if (uip_newdata()) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* Get the DHCP message type (if present), otherwise early-abort */ | 
					
						
							|  |  |  | 		uint8_t DHCPMessageType; | 
					
						
							|  |  |  | 		if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &DHCPMessageType))) | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		uip_ipaddr_t        Netmask, GatewayIPAddress, PreferredClientIP; | 
					
						
							|  |  |  | 		struct uip_eth_addr RemoteMACAddress; | 
					
						
							|  |  |  | 		uint32_t            TransactionID; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Get configured network mask, gateway IP and extract out DHCP transaction ID and remote IP */ | 
					
						
							|  |  |  | 		uip_getnetmask(&Netmask); | 
					
						
							|  |  |  | 		uip_getdraddr(&GatewayIPAddress); | 
					
						
							|  |  |  | 		memcpy(&RemoteMACAddress, &AppData->ClientHardwareAddress, sizeof(struct uip_eth_addr)); | 
					
						
							|  |  |  | 		TransactionID = AppData->TransactionID; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Try to extract out the client's preferred IP address if it is indicated in the packet */ | 
					
						
							|  |  |  | 		if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_REQ_IPADDR, &PreferredClientIP))) | 
					
						
							|  |  |  | 		  memcpy(&PreferredClientIP, &uip_all_zeroes_addr, sizeof(uip_ipaddr_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (DHCPMessageType) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			case DHCP_DISCOVER: | 
					
						
							|  |  |  | 				/* If no preference was made or the preferred IP is already taken, find a new address */ | 
					
						
							|  |  |  | 				if (DHCPServerApp_CheckIfIPLeased(&PreferredClientIP)) | 
					
						
							|  |  |  | 				  DHCPServerApp_GetUnleasedIP(&PreferredClientIP); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Create a new DHCP OFFER packet with the offered IP address */ | 
					
						
							|  |  |  | 				AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_OFFER, &RemoteMACAddress, &PreferredClientIP, TransactionID); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Add network mask and router information to the list of DHCP OFFER packet options */ | 
					
						
							|  |  |  | 				AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK, | 
					
						
							|  |  |  | 													sizeof(uip_ipaddr_t), &Netmask); | 
					
						
							|  |  |  | 				AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER, | 
					
						
							|  |  |  | 					                                sizeof(uip_ipaddr_t), &GatewayIPAddress); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Send the DHCP OFFER packet */ | 
					
						
							|  |  |  | 				uip_poll_conn(BroadcastConnection); | 
					
						
							|  |  |  | 				memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t)); | 
					
						
							|  |  |  | 				uip_udp_send(AppDataSize); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case DHCP_REQUEST: | 
					
						
							|  |  |  | 				/* Check to see if the requested IP address has already been leased to a client */ | 
					
						
							|  |  |  | 				if (!(DHCPServerApp_CheckIfIPLeased(&PreferredClientIP))) | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					/* Create a new DHCP ACK packet to accept the IP address lease */ | 
					
						
							|  |  |  | 					AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_ACK, &RemoteMACAddress, &PreferredClientIP, TransactionID); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					/* Add network mask and router information to the list of DHCP ACK packet options */ | 
					
						
							|  |  |  | 					AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK, | 
					
						
							|  |  |  | 														sizeof(uip_ipaddr_t), &Netmask); | 
					
						
							|  |  |  | 					AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER, | 
					
						
							|  |  |  | 					                                    sizeof(uip_ipaddr_t), &GatewayIPAddress); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					/* Mark the requested IP as leased to a client */ | 
					
						
							|  |  |  | 					DHCPServerApp_LeaseIP(&PreferredClientIP); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					/* Create a new DHCP NAK packet to reject the requested allocation */ | 
					
						
							|  |  |  | 					AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_NAK, &RemoteMACAddress, &uip_all_zeroes_addr, TransactionID); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Send the DHCP ACK or NAK packet */ | 
					
						
							|  |  |  | 				uip_poll_conn(BroadcastConnection); | 
					
						
							|  |  |  | 				memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t)); | 
					
						
							|  |  |  | 				uip_udp_send(AppDataSize); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case DHCP_RELEASE: | 
					
						
							|  |  |  | 				/* Mark the IP address as released in the allocation table */ | 
					
						
							|  |  |  | 				DHCPServerApp_UnleaseIP(&uip_udp_conn->ripaddr); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Fills the DHCP packet response with the appropriate BOOTP header for DHCP. This fills out all the required
 | 
					
						
							|  |  |  |  *  fields, leaving only the additional DHCP options to be added to the packet before it is sent to the DHCP client. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \param[out] DHCPHeader             Location in the packet buffer where the BOOTP header should be written to | 
					
						
							|  |  |  |  *  \param[in]  DHCPMessageType        DHCP Message type, such as DHCP_DISCOVER | 
					
						
							|  |  |  |  *  \param[in]  ClientHardwareAddress  Client MAC address the created transaction should be directed to | 
					
						
							|  |  |  |  *  \param[in]  PreferredClientIP      Preferred IP that should be given to the client if it is unallocated | 
					
						
							|  |  |  |  *  \param[in]  TransactionID          Transaction ID the created transaction should be associated with | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \return Size in bytes of the created DHCP packet | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static uint16_t DHCPServerApp_FillDHCPHeader(DHCP_Header_t* const DHCPHeader, | 
					
						
							|  |  |  |                                              const uint8_t DHCPMessageType, | 
					
						
							|  |  |  |                                              const struct uip_eth_addr* const ClientHardwareAddress, | 
					
						
							|  |  |  | 											 const uip_ipaddr_t* const PreferredClientIP, | 
					
						
							|  |  |  |                                              const uint32_t TransactionID) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Erase existing packet data so that we start will all 0x00 DHCP header data */ | 
					
						
							|  |  |  |  	memset(DHCPHeader, 0, sizeof(DHCP_Header_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	DHCPHeader->Operation             = DHCPMessageType; | 
					
						
							|  |  |  | 	DHCPHeader->HardwareType          = DHCP_HTYPE_ETHERNET; | 
					
						
							|  |  |  | 	DHCPHeader->HardwareAddressLength = sizeof(MACAddress); | 
					
						
							|  |  |  | 	DHCPHeader->Hops                  = 0; | 
					
						
							|  |  |  | 	DHCPHeader->TransactionID         = TransactionID; | 
					
						
							|  |  |  | 	DHCPHeader->ElapsedSeconds        = 0; | 
					
						
							|  |  |  | 	DHCPHeader->Flags                 = 0; | 
					
						
							|  |  |  | 	memcpy(&DHCPHeader->NextServerIP, &uip_hostaddr, sizeof(uip_ipaddr_t)); | 
					
						
							|  |  |  | 	memcpy(&DHCPHeader->YourIP, PreferredClientIP, sizeof(uip_ipaddr_t)); | 
					
						
							|  |  |  | 	memcpy(&DHCPHeader->ClientHardwareAddress, ClientHardwareAddress, sizeof(struct uip_eth_addr)); | 
					
						
							|  |  |  | 	DHCPHeader->Cookie                = DHCP_MAGIC_COOKIE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Add a DHCP message 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Checks to see if the nominated IP address has already been allocated to a client.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \param[in] IPAddress  IP Address whose lease status should be checked | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \return Boolean \c true if the IP has already been leased to a client, \c false otherwise. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static bool DHCPServerApp_CheckIfIPLeased(const uip_ipaddr_t* const IPAddress) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t Byte = (IPAddress->u8[3] / 8); | 
					
						
							|  |  |  | 	uint8_t Mask = (1 << (IPAddress->u8[3] % 8)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure that the requested IP address isn't already leased to the virtual server or another client */ | 
					
						
							|  |  |  | 	if (IPAddress->u8[3] && !(IPAddress->u8[3] == uip_hostaddr.u8[3]) && !(LeasedIPs[Byte] & Mask)) | 
					
						
							|  |  |  | 	  return false; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	  return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Retrieves the next unleased IP in the IP address pool.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \param[out] NewIPAddress  Location where the generated IP Address should be stored | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t* const NewIPAddress) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uip_ipaddr_copy(NewIPAddress, &uip_hostaddr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Look through the current subnet, skipping the broadcast and zero IP addresses */ | 
					
						
							|  |  |  | 	for (uint8_t IP = 1; IP < 254; IP++) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* Update new IP address to lease with the current IP address to test */ | 
					
						
							|  |  |  | 		NewIPAddress->u8[3] = IP; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* If we've found an unleased IP, abort with the updated IP stored for the called */ | 
					
						
							|  |  |  | 		if (!(DHCPServerApp_CheckIfIPLeased(NewIPAddress))) | 
					
						
							|  |  |  | 		  return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Marks the given IP Address as leased in the address pool, so that it will not be
 | 
					
						
							|  |  |  |  *  allocated to another client unless it is first released. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \param[in] IPAddress  IP Address to mark as leased | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void DHCPServerApp_LeaseIP(const uip_ipaddr_t* const IPAddress) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t Byte = (IPAddress->u8[3] / 8); | 
					
						
							|  |  |  | 	uint8_t Mask = (1 << (IPAddress->u8[3] % 8)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Mark the IP address as leased in the allocation table */ | 
					
						
							|  |  |  | 	LeasedIPs[Byte] |= Mask; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Marks the given IP Address as not leased in the address pool, so that it can be
 | 
					
						
							|  |  |  |  *  allocated to another client upon request. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \param[in] IPAddress  IP Address to mark as not leased | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  \pre The IP address must be within the same /24 subnet as the virtual webserver. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void DHCPServerApp_UnleaseIP(const uip_ipaddr_t* const IPAddress) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t Byte = (IPAddress->u8[3] / 8); | 
					
						
							|  |  |  | 	uint8_t Mask = (1 << (IPAddress->u8[3] % 8)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Mark the IP address as unleased in the allocation table */ | 
					
						
							|  |  |  | 	LeasedIPs[Byte] &= ~Mask; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 |