3 Copyright (C) Dean Camera, 2011.
5 dean [at] fourwalledcubicle [dot] com
10 Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
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.
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
31 #if defined(ENABLE_DHCP_SERVER) || defined(__DOXYGEN__)
35 * DHCP Server Application. When connected to the uIP stack, this will send IP configuration settings to a
36 * DHCP client on the network.
39 #define INCLUDE_FROM_DHCPSERVERAPP_C
40 #include "DHCPServerApp.h"
42 struct uip_conn
* BroadcastConnection
;
44 uint8_t LeasedIPs
[255 / 8];
46 /** Initialization function for the DHCP server. */
47 void DHCPServerApp_Init(void)
49 /* Listen on port 67 for DHCP server connections from hosts */
50 uip_listen(HTONS(DHCP_SERVER_PORT
));
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
));
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
));
59 /* Set all IP addresses as unleased */
60 memset(LeasedIPs
, 0x00, sizeof(LeasedIPs
));
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.
66 void DHCPServerApp_Callback(void)
68 DHCP_Header_t
* const AppData
= (DHCP_Header_t
*)uip_appdata
;
69 uint16_t AppDataSize
= 0;
71 uint8_t DHCPMessageType
;
72 if (!(DHCPCommon_GetOption(AppData
->Options
, DHCP_OPTION_MSG_TYPE
, &DHCPMessageType
)))
75 uip_ipaddr_t Netmask
, GatewayIPAddress
;
76 struct uip_eth_addr RemoteMACAddress
;
77 uint32_t TransactionID
;
79 memcpy(&RemoteMACAddress
, &AppData
->ClientHardwareAddress
, sizeof(struct uip_eth_addr
));
80 uip_getnetmask(&Netmask
);
81 uip_getdraddr(&GatewayIPAddress
);
82 TransactionID
= AppData
->TransactionID
;
84 switch (DHCPMessageType
)
87 AppDataSize
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_OFFER
, &RemoteMACAddress
, TransactionID
);
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
);
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
);
101 if (!(DHCPServerApp_CheckIfIPLeased(&AppData
->YourIP
)))
103 AppDataSize
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_ACK
, &RemoteMACAddress
, TransactionID
);
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
);
110 DHCPServerApp_LeaseIP(&AppData
->YourIP
);
114 AppDataSize
+= DHCPServerApp_FillDHCPHeader(AppData
, DHCP_NAK
, &RemoteMACAddress
, TransactionID
);
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
);
124 /* Mark the IP address as released in the allocation table */
125 DHCPServerApp_UnleaseIP(&uip_udp_conn
->ripaddr
);
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.
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
138 * \return Size in bytes of the created DHCP packet
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
)
145 /* Erase existing packet data so that we start will all 0x00 DHCP header data */
146 memset(DHCPHeader
, 0, sizeof(DHCP_Header_t
));
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
;
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
;
167 /* Calculate the total number of bytes added to the outgoing packet */
168 return (sizeof(DHCP_Header_t
) + 4);
171 /** Checks to see if the nominated IP address has already been allocated to a client.
173 * \param[in] IPAddress IP Address whose lease status should be checked
175 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
177 * \return Boolean true if the IP has already been leased to a client, false otherwise.
179 static bool DHCPServerApp_CheckIfIPLeased(uip_ipaddr_t
* IPAddress
)
181 uint8_t Byte
= (IPAddress
->u8
[3] / 8);
182 uint8_t Mask
= (1 << (IPAddress
->u8
[3] % 8));
184 if (!(IPAddress
->u8
[3] == uip_hostaddr
.u8
[3]) && !(LeasedIPs
[Byte
] & Mask
))
190 /** Retrieves the next unleased IP in the IP address pool.
192 * \param[out] NewIPAddress Location where the generated IP Address should be stored
194 static void DHCPServerApp_GetUnleasedIP(uip_ipaddr_t
* NewIPAddress
)
196 uip_ipaddr_copy(NewIPAddress
, &uip_hostaddr
);
198 for (uint8_t IP
= 1; IP
< 254; IP
++)
200 NewIPAddress
->u8
[3] = IP
;
202 if (!(DHCPServerApp_CheckIfIPLeased(NewIPAddress
)))
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.
210 * \param[in] IPAddress IP Address to mark as leased
212 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
214 static void DHCPServerApp_LeaseIP(uip_ipaddr_t
* IPAddress
)
216 uint8_t Byte
= (IPAddress
->u8
[3] / 8);
217 uint8_t Mask
= (1 << (IPAddress
->u8
[3] % 8));
219 LeasedIPs
[Byte
] |= Mask
;
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.
225 * \param[in] IPAddress IP Address to mark as not leased
227 * \pre The IP address must be within the same /24 subnet as the virtual webserver.
229 static void DHCPServerApp_UnleaseIP(uip_ipaddr_t
* IPAddress
)
231 uint8_t Byte
= (IPAddress
->u8
[3] / 8);
232 uint8_t Mask
= (1 << (IPAddress
->u8
[3] % 8));
234 LeasedIPs
[Byte
] &= ~Mask
;