84c57d0a2ed5f3db28ef4125592ad8b0b4a6a4a8
[pub/USBasp.git] / Projects / Webserver / Lib / DHCPServerApp.c
1 /*
2 LUFA Library
3 Copyright (C) Dean Camera, 2011.
4
5 dean [at] fourwalledcubicle [dot] com
6 www.lufa-lib.org
7 */
8
9 /*
10 Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
11
12 Permission to use, copy, modify, distribute, and sell this
13 software and its documentation for any purpose is hereby granted
14 without fee, provided that the above copyright notice appear in
15 all copies and that both that the copyright notice and this
16 permission notice and warranty disclaimer appear in supporting
17 documentation, and that the name of the author not be used in
18 advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
20
21 The author disclaim all warranties with regard to this
22 software, including all implied warranties of merchantability
23 and fitness. In no event shall the author be liable for any
24 special, indirect or consequential damages or any damages
25 whatsoever resulting from loss of use, data or profits, whether
26 in an action of contract, negligence or other tortious action,
27 arising out of or in connection with the use or performance of
28 this software.
29 */
30
31 #if defined(ENABLE_DHCP_SERVER) || defined(__DOXYGEN__)
32
33 /** \file
34 *
35 * DHCP Server Application. When connected to the uIP stack, this will send IP configuration settings to a
36 * DHCP client on the network.
37 */
38
39 #define INCLUDE_FROM_DHCPSERVERAPP_C
40 #include "DHCPServerApp.h"
41
42 struct uip_conn* BroadcastConnection;
43
44 uint8_t LeasedIPs[255 / 8];
45
46 /** Initialization function for the DHCP server. */
47 void DHCPServerApp_Init(void)
48 {
49 /* Listen on port 67 for DHCP server connections from hosts */
50 uip_listen(HTONS(DHCP_SERVER_PORT));
51
52 /* Create a new UDP connection to the DHCP server port for the DHCP solicitation */
53 struct uip_udp_conn* BroadcastConnection = uip_udp_new(&uip_broadcast_addr, HTONS(DHCP_CLIENT_PORT));
54
55 /* If the connection was successfully created, bind it to the local DHCP client port */
56 if (BroadcastConnection != NULL)
57 uip_udp_bind(BroadcastConnection, HTONS(DHCP_SERVER_PORT));
58
59 /* Set all IP addresses as unleased */
60 memset(LeasedIPs, 0x00, sizeof(LeasedIPs));
61 }
62
63 /** uIP stack application callback for the DHCP server. This function must be called each time the TCP/IP stack
64 * needs a UDP packet to be processed.
65 */
66 void DHCPServerApp_Callback(void)
67 {
68 DHCP_Header_t* const AppData = (DHCP_Header_t*)uip_appdata;
69 uint16_t AppDataSize = 0;
70
71 uint8_t DHCPMessageType;
72 if (!(DHCPCommon_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &DHCPMessageType)))
73 return;
74
75 uip_ipaddr_t Netmask, GatewayIPAddress;
76 struct uip_eth_addr RemoteMACAddress;
77 uint32_t TransactionID;
78
79 memcpy(&RemoteMACAddress, &AppData->ClientHardwareAddress, sizeof(struct uip_eth_addr));
80 uip_getnetmask(&Netmask);
81 uip_getdraddr(&GatewayIPAddress);
82 TransactionID = AppData->TransactionID;
83
84 switch (DHCPMessageType)
85 {
86 case DHCP_DISCOVER:
87 AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_OFFER, &RemoteMACAddress, TransactionID);
88
89 AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK,
90 sizeof(uip_ipaddr_t), &Netmask);
91 AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER,
92 sizeof(uip_ipaddr_t), &GatewayIPAddress);
93
94 /* Send the DHCP OFFER packet */
95 uip_poll_conn(BroadcastConnection);
96 memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));
97 uip_udp_send(AppDataSize);
98
99 break;
100 case DHCP_REQUEST:
101 if (!(DHCPServerApp_CheckIfIPLeased(&AppData->YourIP)))
102 {
103 AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_ACK, &RemoteMACAddress, TransactionID);
104
105 AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK,
106 sizeof(uip_ipaddr_t), &Netmask);
107 AppDataSize += DHCPCommon_SetOption(AppData->Options, DHCP_OPTION_ROUTER,
108 sizeof(uip_ipaddr_t), &GatewayIPAddress);
109
110 DHCPServerApp_LeaseIP(&AppData->YourIP);
111 }
112 else
113 {
114 AppDataSize += DHCPServerApp_FillDHCPHeader(AppData, DHCP_NAK, &RemoteMACAddress, TransactionID);
115 }
116
117 /* Send the DHCP ACK or NAK packet */
118 uip_poll_conn(BroadcastConnection);
119 memcpy(&uip_udp_conn->ripaddr, &uip_broadcast_addr, sizeof(uip_ipaddr_t));
120 uip_udp_send(AppDataSize);
121
122 break;
123 case DHCP_RELEASE:
124 /* Mark the IP address as released in the allocation table */
125 DHCPServerApp_UnleaseIP(&uip_udp_conn->ripaddr);
126 break;
127 }
128 }
129
130 /** Fills the DHCP packet response with the appropriate BOOTP header for DHCP. This fills out all the required
131 * fields, leaving only the additional DHCP options to be added to the packet before it is sent to the DHCP client.
132 *
133 * \param[out] DHCPHeader Location in the packet buffer where the BOOTP header should be written to
134 * \param[in] DHCPMessageType DHCP Message type, such as DHCP_DISCOVER
135 * \param[in] ClientHardwareAddress Client MAC address the created transaction should be directed to
136 * \param[in] TransactionID Transaction ID the created transaction should be associated with
137 *
138 * \return Size in bytes of the created DHCP packet
139 */
140 static uint16_t DHCPServerApp_FillDHCPHeader(DHCP_Header_t* const DHCPHeader,
141 const uint8_t DHCPMessageType,
142 struct uip_eth_addr* ClientHardwareAddress,
143 uint32_t TransactionID)
144 {
145 /* Erase existing packet data so that we start will all 0x00 DHCP header data */
146 memset(DHCPHeader, 0, sizeof(DHCP_Header_t));
147
148 DHCPHeader->Operation = DHCPMessageType;
149 DHCPHeader->HardwareType = DHCP_HTYPE_ETHERNET;
150 DHCPHeader->HardwareAddressLength = sizeof(MACAddress);
151 DHCPHeader->Hops = 0;
152 DHCPHeader->TransactionID = TransactionID;
153 DHCPHeader->ElapsedSeconds = 0;
154 DHCPHeader->Flags = 0;
155 memcpy(&DHCPHeader->NextServerIP, &uip_hostaddr, sizeof(uip_ipaddr_t));
156 if (uip_ipaddr_cmp(&DHCPHeader->YourIP, &uip_all_zeroes_addr))
157 DHCPServerApp_GetUnleasedIP(&DHCPHeader->YourIP);
158 memcpy(&DHCPHeader->ClientHardwareAddress, ClientHardwareAddress, sizeof(struct uip_eth_addr));
159 DHCPHeader->Cookie = DHCP_MAGIC_COOKIE;
160
161 /* Add a DHCP message type and terminator options to the start of the DHCP options field */
162 DHCPHeader->Options[0] = DHCP_OPTION_MSG_TYPE;
163 DHCPHeader->Options[1] = 1;
164 DHCPHeader->Options[2] = DHCPMessageType;
165 DHCPHeader->Options[3] = DHCP_OPTION_END;
166
167 /* Calculate the total number of bytes added to the outgoing packet */
168 return (sizeof(DHCP_Header_t) + 4);
169 }
170
171 /** Checks to see if the nominated IP address has already been allocated to a client.
172 *
173 * \param[in] IPAddress IP Address whose lease status should be checked
174 *
175 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
176 *
177 * \return Boolean true if the IP has already been leased to a client, false otherwise.
178 */
179 static bool DHCPServerApp_CheckIfIPLeased(uip_ipaddr_t* IPAddress)
180 {
181 uint8_t Byte = (IPAddress->u8[3] / 8);
182 uint8_t Mask = (1 << (IPAddress->u8[3] % 8));
183
184 if (!(IPAddress->u8[3] == uip_hostaddr.u8[3]) && !(LeasedIPs[Byte] & Mask))
185 return false;
186 else
187 return true;
188 }
189
190 /** Retrieves the next unleased IP in the IP address pool.
191 *
192 * \param[out] NewIPAddress Location where the generated IP Address should be stored
193 */
194 static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t* NewIPAddress)
195 {
196 uip_ipaddr_copy(NewIPAddress, &uip_hostaddr);
197
198 for (uint8_t IP = 1; IP < 254; IP++)
199 {
200 NewIPAddress->u8[3] = IP;
201
202 if (!(DHCPServerApp_CheckIfIPLeased(NewIPAddress)))
203 return;
204 }
205 }
206
207 /** Marks the given IP Address as leased in the address pool, so that it will not be
208 * allocated to another client unless it is first released.
209 *
210 * \param[in] IPAddress IP Address to mark as leased
211 *
212 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
213 */
214 static void DHCPServerApp_LeaseIP(uip_ipaddr_t* IPAddress)
215 {
216 uint8_t Byte = (IPAddress->u8[3] / 8);
217 uint8_t Mask = (1 << (IPAddress->u8[3] % 8));
218
219 LeasedIPs[Byte] |= Mask;
220 }
221
222 /** Marks the given IP Address as not leased in the address pool, so that it can be
223 * allocated to another client upon request.
224 *
225 * \param[in] IPAddress IP Address to mark as not leased
226 *
227 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
228 */
229 static void DHCPServerApp_UnleaseIP(uip_ipaddr_t* IPAddress)
230 {
231 uint8_t Byte = (IPAddress->u8[3] / 8);
232 uint8_t Mask = (1 << (IPAddress->u8[3] % 8));
233
234 LeasedIPs[Byte] &= ~Mask;
235 }
236 #endif
237