diff options
| author | Jack Humbert <[email protected]> | 2017-07-07 11:55:23 -0400 | 
|---|---|---|
| committer | Jack Humbert <[email protected]> | 2017-07-07 11:55:23 -0400 | 
| commit | 60b30c036397cb5627fa374bb930794b225daa29 (patch) | |
| tree | e156c2d6b27f01db6985f2cc560c9c345d48fdac /Projects/Webserver/Lib/DHCPServerApp.c | |
| download | qmk_firmware-60b30c036397cb5627fa374bb930794b225daa29.tar.gz qmk_firmware-60b30c036397cb5627fa374bb930794b225daa29.zip | |
Squashed 'lib/lufa/' content from commit 385d40300
git-subtree-dir: lib/lufa
git-subtree-split: 385d4030035dbaf41591309dbde47653bd03841b
Diffstat (limited to 'Projects/Webserver/Lib/DHCPServerApp.c')
| -rw-r--r-- | Projects/Webserver/Lib/DHCPServerApp.c | 265 | 
1 files changed, 265 insertions, 0 deletions
| diff --git a/Projects/Webserver/Lib/DHCPServerApp.c b/Projects/Webserver/Lib/DHCPServerApp.c new file mode 100644 index 0000000000..fea54ddb0e --- /dev/null +++ b/Projects/Webserver/Lib/DHCPServerApp.c @@ -0,0 +1,265 @@ +/* +             LUFA Library +     Copyright (C) Dean Camera, 2017. + +  dean [at] fourwalledcubicle [dot] com +           www.lufa-lib.org +*/ + +/* +  Copyright 2017  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 + | 
