xref: /btstack/3rd-party/lwip/dhcp-server/dhserver.c (revision 88cd67309cc94af545a96dac921d5ef21b3aa56e)
1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2015 by Sergey Fetisov <[email protected]>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #include "dhserver.h"
26 
27 /* DHCP message type */
28 #define DHCP_DISCOVER       1
29 #define DHCP_OFFER          2
30 #define DHCP_REQUEST        3
31 #define DHCP_DECLINE        4
32 #define DHCP_ACK            5
33 #define DHCP_NAK            6
34 #define DHCP_RELEASE        7
35 #define DHCP_INFORM         8
36 
37 /* DHCP options */
38 enum DHCP_OPTIONS
39 {
40 	DHCP_PAD                    = 0,
41 	DHCP_SUBNETMASK             = 1,
42 	DHCP_ROUTER                 = 3,
43 	DHCP_DNSSERVER              = 6,
44 	DHCP_HOSTNAME               = 12,
45 	DHCP_DNSDOMAIN              = 15,
46 	DHCP_MTU                    = 26,
47 	DHCP_BROADCAST              = 28,
48 	DHCP_PERFORMROUTERDISC      = 31,
49 	DHCP_STATICROUTE            = 33,
50 	DHCP_NISDOMAIN              = 40,
51 	DHCP_NISSERVER              = 41,
52 	DHCP_NTPSERVER              = 42,
53 	DHCP_VENDOR                 = 43,
54 	DHCP_IPADDRESS              = 50,
55 	DHCP_LEASETIME              = 51,
56 	DHCP_OPTIONSOVERLOADED      = 52,
57 	DHCP_MESSAGETYPE            = 53,
58 	DHCP_SERVERID               = 54,
59 	DHCP_PARAMETERREQUESTLIST   = 55,
60 	DHCP_MESSAGE                = 56,
61 	DHCP_MAXMESSAGESIZE         = 57,
62 	DHCP_RENEWALTIME            = 58,
63 	DHCP_REBINDTIME             = 59,
64 	DHCP_CLASSID                = 60,
65 	DHCP_CLIENTID               = 61,
66 	DHCP_USERCLASS              = 77,  /* RFC 3004 */
67 	DHCP_FQDN                   = 81,
68 	DHCP_DNSSEARCH              = 119, /* RFC 3397 */
69 	DHCP_CSR                    = 121, /* RFC 3442 */
70 	DHCP_MSCSR                  = 249, /* MS code for RFC 3442 */
71 	DHCP_END                    = 255
72 };
73 
74 typedef struct
75 {
76     uint8_t  dp_op;           /* packet opcode type */
77     uint8_t  dp_htype;        /* hardware addr type */
78     uint8_t  dp_hlen;         /* hardware addr length */
79     uint8_t  dp_hops;         /* gateway hops */
80     uint32_t dp_xid;          /* transaction ID */
81     uint16_t dp_secs;         /* seconds since boot began */
82     uint16_t dp_flags;
83     uint8_t  dp_ciaddr[4];    /* client IP address */
84     uint8_t  dp_yiaddr[4];    /* 'your' IP address */
85     uint8_t  dp_siaddr[4];    /* server IP address */
86     uint8_t  dp_giaddr[4];    /* gateway IP address */
87     uint8_t  dp_chaddr[16];   /* client hardware address */
88     uint8_t  dp_legacy[192];
89     uint8_t  dp_magic[4];
90     uint8_t  dp_options[275]; /* options area */
91 } DHCP_TYPE;
92 
93 DHCP_TYPE dhcp_data;
94 static struct udp_pcb *pcb = NULL;
95 static dhcp_config_t *config = NULL;
96 
97 char magic_cookie[] = {0x63,0x82,0x53,0x63};
98 
get_addr32(const uint8_t addr[4])99 static uint32_t get_addr32(const uint8_t addr[4]) {
100 	return PP_HTONL(LWIP_MAKEU32(addr[0],addr[1],addr[2],addr[3]));
101 }
102 
set_addr32(uint8_t * dst,uint32_t src)103 static void set_addr32(uint8_t *dst, uint32_t src) {
104 	memcpy(dst, &src, 4);
105 }
106 
entry_by_ip(uint32_t ip)107 static dhcp_entry_t *entry_by_ip(uint32_t ip)
108 {
109 	int i;
110 	for (i = 0; i < config->num_entry; i++)
111 		if (get_addr32(config->entries[i].addr) == ip)
112 			return &config->entries[i];
113 	return NULL;
114 }
115 
entry_by_mac(uint8_t * mac)116 static dhcp_entry_t *entry_by_mac(uint8_t *mac)
117 {
118 	int i;
119 	for (i = 0; i < config->num_entry; i++)
120 		if (memcmp(config->entries[i].mac, mac, 6) == 0)
121 			return &config->entries[i];
122 	return NULL;
123 }
124 
is_vacant(dhcp_entry_t * entry)125 static __inline bool is_vacant(dhcp_entry_t *entry)
126 {
127 	return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
128 }
129 
vacant_address(void)130 static dhcp_entry_t *vacant_address(void)
131 {
132 	int i;
133 	for (i = 0; i < config->num_entry; i++)
134 		if (is_vacant(config->entries + i))
135 			return config->entries + i;
136 	return NULL;
137 }
138 
free_entry(dhcp_entry_t * entry)139 static __inline void free_entry(dhcp_entry_t *entry)
140 {
141 	memset(entry->mac, 0, 6);
142 }
143 
find_dhcp_option(uint8_t * attrs,int size,uint8_t attr)144 static uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
145 {
146 	int i = 0;
147 	while ((i + 1) < size)
148 	{
149 		int next = i + attrs[i + 1] + 2;
150 		if (next > size) return NULL;
151 		if (attrs[i] == attr)
152 			return attrs + i;
153 		i = next;
154 	}
155 	return NULL;
156 }
157 
fill_options(void * dest,uint8_t msg_type,const char * domain,uint32_t dns,int lease_time,uint32_t serverid,uint32_t router,uint32_t subnet)158 static int fill_options(void *dest,
159 	uint8_t msg_type,
160 	const char *domain,
161 	uint32_t dns,
162 	int lease_time,
163 	uint32_t serverid,
164 	uint32_t router,
165 	uint32_t subnet)
166 {
167 	uint8_t *ptr = (uint8_t *)dest;
168 	/* ACK message type */
169 	*ptr++ = 53;
170 	*ptr++ = 1;
171 	*ptr++ = msg_type;
172 
173 	/* dhcp server identifier */
174 	*ptr++ = DHCP_SERVERID;
175 	*ptr++ = 4;
176 	set_addr32(ptr, serverid);
177 	ptr += 4;
178 
179 	/* lease time */
180 	*ptr++ = DHCP_LEASETIME;
181 	*ptr++ = 4;
182 	*ptr++ = (lease_time >> 24) & 0xFF;
183 	*ptr++ = (lease_time >> 16) & 0xFF;
184 	*ptr++ = (lease_time >> 8) & 0xFF;
185 	*ptr++ = (lease_time >> 0) & 0xFF;
186 
187 	/* subnet mask */
188 	*ptr++ = DHCP_SUBNETMASK;
189 	*ptr++ = 4;
190 	set_addr32(ptr, subnet);
191 	ptr += 4;
192 
193 	/* router */
194 	if (router != 0)
195 	{
196 		*ptr++ = DHCP_ROUTER;
197 		*ptr++ = 4;
198 		set_addr32(ptr, router);
199 		ptr += 4;
200 	}
201 
202 	/* domain name */
203 	if (domain != NULL)
204 	{
205 		int len = strlen(domain);
206 		*ptr++ = DHCP_DNSDOMAIN;
207 		*ptr++ = len;
208 		memcpy(ptr, domain, len);
209 		ptr += len;
210 	}
211 
212 	/* domain name server (DNS) */
213 	if (dns != 0)
214 	{
215 		*ptr++ = DHCP_DNSSERVER;
216 		*ptr++ = 4;
217 		set_addr32(ptr, dns);
218 		ptr += 4;
219 	}
220 
221 	/* end */
222 	*ptr++ = DHCP_END;
223 	return ptr - (uint8_t *)dest;
224 }
225 
udp_recv_proc(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)226 static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
227 {
228 	(void) arg;
229 	(void) addr;
230 
231 	uint8_t *ptr;
232 	dhcp_entry_t *entry;
233 	struct pbuf *pp;
234 	struct netif *nif;
235 
236 	unsigned int n = p->len;
237 	if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
238 	memcpy(&dhcp_data, p->payload, n);
239 	switch (dhcp_data.dp_options[2])
240 	{
241 		case DHCP_DISCOVER:
242 			entry = entry_by_mac(dhcp_data.dp_chaddr);
243 			if (entry == NULL) entry = vacant_address();
244 			if (entry == NULL) break;
245 
246 			dhcp_data.dp_op = 2; /* reply */
247 			dhcp_data.dp_secs = 0;
248 			dhcp_data.dp_flags = 0;
249 			set_addr32(dhcp_data.dp_yiaddr, get_addr32(entry->addr));
250 			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
251 
252 			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
253 
254 			fill_options(dhcp_data.dp_options,
255 				DHCP_OFFER,
256 				config->domain,
257 				get_addr32(config->dns),
258 				entry->lease,
259 				get_addr32(config->addr),
260 				get_addr32(config->addr),
261 				get_addr32(entry->subnet));
262 
263 			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
264 			if (pp == NULL) break;
265 			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
266 			nif = ip_current_input_netif();
267 			if (nif) {
268 				udp_sendto_if(upcb, pp, IP_ADDR_BROADCAST, port, nif);
269 			} else {
270 				udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
271 			}
272 			pbuf_free(pp);
273 			break;
274 
275 		case DHCP_REQUEST:
276 			/* 1. find requested ipaddr in option list */
277 			ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
278 			if (ptr == NULL) break;
279 			if (ptr[1] != 4) break;
280 			ptr += 2;
281 
282 			/* 2. does hw-address registered? */
283 			entry = entry_by_mac(dhcp_data.dp_chaddr);
284 			if (entry != NULL) free_entry(entry);
285 
286 			/* 3. find requested ipaddr */
287 			entry = entry_by_ip(get_addr32(ptr));
288 			if (entry == NULL) break;
289 			if (!is_vacant(entry)) break;
290 
291 			/* 4. fill struct fields */
292 			memcpy(dhcp_data.dp_yiaddr, ptr, 4);
293 			dhcp_data.dp_op = 2; /* reply */
294 			dhcp_data.dp_secs = 0;
295 			dhcp_data.dp_flags = 0;
296 			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
297 
298 			/* 5. fill options */
299 			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
300 
301 			fill_options(dhcp_data.dp_options,
302 				DHCP_ACK,
303 				config->domain,
304 				get_addr32(config->dns),
305 				entry->lease,
306 				get_addr32(config->addr),
307 				get_addr32(config->addr),
308 				get_addr32(entry->subnet));
309 
310 			/* 6. send ACK */
311 			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
312 			if (pp == NULL) break;
313 			memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
314 			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
315 			nif = ip_current_input_netif();
316 			if (nif) {
317 				udp_sendto_if(upcb, pp, IP_ADDR_BROADCAST, port, nif);
318 			} else {
319 				udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
320 			}
321 			pbuf_free(pp);
322 			break;
323 
324 		default:
325 				break;
326 	}
327 	pbuf_free(p);
328 }
329 
dhserv_init(dhcp_config_t * c)330 err_t dhserv_init(dhcp_config_t *c)
331 {
332 	err_t err;
333 	// udp_init(); already called from lwip_init
334 	dhserv_free();
335 	pcb = udp_new();
336 	if (pcb == NULL)
337 		return ERR_MEM;
338 	err = udp_bind(pcb, IP_ADDR_ANY, c->port);
339 	if (err != ERR_OK)
340 	{
341 		dhserv_free();
342 		return err;
343 	}
344 	udp_recv(pcb, udp_recv_proc, NULL);
345 	config = c;
346 	return ERR_OK;
347 }
348 
dhserv_free(void)349 void dhserv_free(void)
350 {
351 	if (pcb == NULL) return;
352 	udp_remove(pcb);
353 	pcb = NULL;
354 }
355