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