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