mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-11-03 23:02:34 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			266 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
             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
 | 
						|
 |