xref: /btstack/3rd-party/lwip/dhcp-server/dhserver.c (revision cd5f23a3250874824c01a2b3326a9522fea3f99f)
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 
99 static dhcp_entry_t *entry_by_ip(uint32_t ip)
100 {
101 	int i;
102 	for (i = 0; i < config->num_entry; i++)
103 		if (*(uint32_t *)config->entries[i].addr == ip)
104 			return &config->entries[i];
105 	return NULL;
106 }
107 
108 static dhcp_entry_t *entry_by_mac(uint8_t *mac)
109 {
110 	int i;
111 	for (i = 0; i < config->num_entry; i++)
112 		if (memcmp(config->entries[i].mac, mac, 6) == 0)
113 			return &config->entries[i];
114 	return NULL;
115 }
116 
117 static __inline bool is_vacant(dhcp_entry_t *entry)
118 {
119 	return memcmp("\0\0\0\0\0", entry->mac, 6) == 0;
120 }
121 
122 static dhcp_entry_t *vacant_address(void)
123 {
124 	int i;
125 	for (i = 0; i < config->num_entry; i++)
126 		if (is_vacant(config->entries + i))
127 			return config->entries + i;
128 	return NULL;
129 }
130 
131 static __inline void free_entry(dhcp_entry_t *entry)
132 {
133 	memset(entry->mac, 0, 6);
134 }
135 
136 static uint8_t *find_dhcp_option(uint8_t *attrs, int size, uint8_t attr)
137 {
138 	int i = 0;
139 	while ((i + 1) < size)
140 	{
141 		int next = i + attrs[i + 1] + 2;
142 		if (next > size) return NULL;
143 		if (attrs[i] == attr)
144 			return attrs + i;
145 		i = next;
146 	}
147 	return NULL;
148 }
149 
150 static int fill_options(void *dest,
151 	uint8_t msg_type,
152 	const char *domain,
153 	uint32_t dns,
154 	int lease_time,
155 	uint32_t serverid,
156 	uint32_t router,
157 	uint32_t subnet)
158 {
159 	uint8_t *ptr = (uint8_t *)dest;
160 	/* ACK message type */
161 	*ptr++ = 53;
162 	*ptr++ = 1;
163 	*ptr++ = msg_type;
164 
165 	/* dhcp server identifier */
166 	*ptr++ = DHCP_SERVERID;
167 	*ptr++ = 4;
168 	*(uint32_t *)ptr = serverid;
169 	ptr += 4;
170 
171 	/* lease time */
172 	*ptr++ = DHCP_LEASETIME;
173 	*ptr++ = 4;
174 	*ptr++ = (lease_time >> 24) & 0xFF;
175 	*ptr++ = (lease_time >> 16) & 0xFF;
176 	*ptr++ = (lease_time >> 8) & 0xFF;
177 	*ptr++ = (lease_time >> 0) & 0xFF;
178 
179 	/* subnet mask */
180 	*ptr++ = DHCP_SUBNETMASK;
181 	*ptr++ = 4;
182 	*(uint32_t *)ptr = subnet;
183 	ptr += 4;
184 
185 	/* router */
186 	if (router != 0)
187 	{
188 		*ptr++ = DHCP_ROUTER;
189 		*ptr++ = 4;
190 		*(uint32_t *)ptr = router;
191 		ptr += 4;
192 	}
193 
194 	/* domain name */
195 	if (domain != NULL)
196 	{
197 		int len = strlen(domain);
198 		*ptr++ = DHCP_DNSDOMAIN;
199 		*ptr++ = len;
200 		memcpy(ptr, domain, len);
201 		ptr += len;
202 	}
203 
204 	/* domain name server (DNS) */
205 	if (dns != 0)
206 	{
207 		*ptr++ = DHCP_DNSSERVER;
208 		*ptr++ = 4;
209 		*(uint32_t *)ptr = dns;
210 		ptr += 4;
211 	}
212 
213 	/* end */
214 	*ptr++ = DHCP_END;
215 	return ptr - (uint8_t *)dest;
216 }
217 
218 static void udp_recv_proc(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
219 {
220 	(void) arg;
221 	(void) addr;
222 
223 	uint8_t *ptr;
224 	dhcp_entry_t *entry;
225 	struct pbuf *pp;
226 
227 	unsigned int n = p->len;
228 	if (n > sizeof(dhcp_data)) n = sizeof(dhcp_data);
229 	memcpy(&dhcp_data, p->payload, n);
230 	switch (dhcp_data.dp_options[2])
231 	{
232 		case DHCP_DISCOVER:
233 			entry = entry_by_mac(dhcp_data.dp_chaddr);
234 			if (entry == NULL) entry = vacant_address();
235 			if (entry == NULL) break;
236 
237 			dhcp_data.dp_op = 2; /* reply */
238 			dhcp_data.dp_secs = 0;
239 			dhcp_data.dp_flags = 0;
240 			*(uint32_t *)dhcp_data.dp_yiaddr = *(uint32_t *)entry->addr;
241 			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
242 
243 			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
244 
245 			fill_options(dhcp_data.dp_options,
246 				DHCP_OFFER,
247 				config->domain,
248 				*(uint32_t *)config->dns,
249 				entry->lease,
250 				*(uint32_t *)config->addr,
251 				*(uint32_t *)config->addr,
252 				*(uint32_t *)entry->subnet);
253 
254 			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
255 			if (pp == NULL) break;
256 			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
257 			udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
258 			pbuf_free(pp);
259 			break;
260 
261 		case DHCP_REQUEST:
262 			/* 1. find requested ipaddr in option list */
263 			ptr = find_dhcp_option(dhcp_data.dp_options, sizeof(dhcp_data.dp_options), DHCP_IPADDRESS);
264 			if (ptr == NULL) break;
265 			if (ptr[1] != 4) break;
266 			ptr += 2;
267 
268 			/* 2. does hw-address registered? */
269 			entry = entry_by_mac(dhcp_data.dp_chaddr);
270 			if (entry != NULL) free_entry(entry);
271 
272 			/* 3. find requested ipaddr */
273 			entry = entry_by_ip(*(uint32_t *)ptr);
274 			if (entry == NULL) break;
275 			if (!is_vacant(entry)) break;
276 
277 			/* 4. fill struct fields */
278 			memcpy(dhcp_data.dp_yiaddr, ptr, 4);
279 			dhcp_data.dp_op = 2; /* reply */
280 			dhcp_data.dp_secs = 0;
281 			dhcp_data.dp_flags = 0;
282 			memcpy(dhcp_data.dp_magic, magic_cookie, 4);
283 
284 			/* 5. fill options */
285 			memset(dhcp_data.dp_options, 0, sizeof(dhcp_data.dp_options));
286 
287 			fill_options(dhcp_data.dp_options,
288 				DHCP_ACK,
289 				config->domain,
290 				*(uint32_t *)config->dns,
291 				entry->lease,
292 				*(uint32_t *)config->addr,
293 				*(uint32_t *)config->addr,
294 				*(uint32_t *)entry->subnet);
295 
296 			/* 6. send ACK */
297 			pp = pbuf_alloc(PBUF_TRANSPORT, sizeof(dhcp_data), PBUF_POOL);
298 			if (pp == NULL) break;
299 			memcpy(entry->mac, dhcp_data.dp_chaddr, 6);
300 			memcpy(pp->payload, &dhcp_data, sizeof(dhcp_data));
301 			udp_sendto(upcb, pp, IP_ADDR_BROADCAST, port);
302 			pbuf_free(pp);
303 			break;
304 
305 		default:
306 				break;
307 	}
308 	pbuf_free(p);
309 }
310 
311 err_t dhserv_init(dhcp_config_t *c)
312 {
313 	err_t err;
314 	// udp_init(); already called from lwip_init
315 	dhserv_free();
316 	pcb = udp_new();
317 	if (pcb == NULL)
318 		return ERR_MEM;
319 	err = udp_bind(pcb, IP_ADDR_ANY, c->port);
320 	if (err != ERR_OK)
321 	{
322 		dhserv_free();
323 		return err;
324 	}
325 	udp_recv(pcb, udp_recv_proc, NULL);
326 	config = c;
327 	return ERR_OK;
328 }
329 
330 void dhserv_free(void)
331 {
332 	if (pcb == NULL) return;
333 	udp_remove(pcb);
334 	pcb = NULL;
335 }
336