1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.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
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell 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
14 * all 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
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lib-core.h"
26 #include "private-lib-system-dhcpclient.h"
27
28 typedef enum {
29 LDHC_INIT_REBOOT,
30 LDHC_REBOOTING, /* jitterwait */
31 LDHC_INIT, /* issue DHCPDISCOVER */
32 LDHC_SELECTING,
33 LDHC_REQUESTING,
34 LDHC_REBINDING,
35 LDHC_BOUND,
36 LDHC_RENEWING
37 } lws_dhcpc_state_t;
38
39 enum {
40 LWSDHCPDISCOVER = 1,
41 LWSDHCPOFFER,
42 LWSDHCPREQUEST,
43 LWSDHCPDECLINE,
44 LWSDHCPACK,
45 LWSDHCPNACK,
46 LWSDHCPRELEASE,
47
48 IPV4_PROPOSED = 0,
49 IPV4_SERVER,
50 IPV4_ROUTER,
51 IPV4_SUBNET_MASK,
52 IPV4_BROADCAST,
53 IPV4_TIME_SERVER,
54 IPV4_DNS_SRV_1,
55 IPV4_DNS_SRV_2,
56 IPV4_DNS_SRV_3,
57 IPV4_DNS_SRV_4,
58 IPV4_LEASE_SECS,
59 IPV4_REBINDING_SECS,
60 IPV4_RENEWAL_SECS,
61
62 _IPV4_COUNT,
63
64 LWSDHCPOPT_PAD = 0,
65 LWSDHCPOPT_SUBNET_MASK = 1,
66 LWSDHCPOPT_TIME_OFFSET = 2,
67 LWSDHCPOPT_ROUTER = 3,
68 LWSDHCPOPT_TIME_SERVER = 4,
69 LWSDHCPOPT_NAME_SERVER = 5,
70 LWSDHCPOPT_DNSERVER = 6,
71 LWSDHCPOPT_LOG_SERVER = 7,
72 LWSDHCPOPT_COOKIE_SERVER = 8,
73 LWSDHCPOPT_LPR_SERVER = 9,
74 LWSDHCPOPT_IMPRESS_SERVER = 10,
75 LWSDHCPOPT_RESLOC_SERVER = 11,
76 LWSDHCPOPT_HOST_NAME = 12,
77 LWSDHCPOPT_BOOTFILE_SIZE = 13,
78 LWSDHCPOPT_MERIT_DUMP_FILE = 14,
79 LWSDHCPOPT_DOMAIN_NAME = 15,
80 LWSDHCPOPT_SWAP_SERVER = 16,
81 LWSDHCPOPT_ROOT_PATH = 17,
82 LWSDHCPOPT_EXTENSIONS_PATH = 18,
83 LWSDHCPOPT_BROADCAST_ADS = 28,
84
85 LWSDHCPOPT_REQUESTED_ADS = 50,
86 LWSDHCPOPT_LEASE_TIME = 51,
87 LWSDHCPOPT_OPTION_OVERLOAD = 52,
88 LWSDHCPOPT_MESSAGE_TYPE = 53,
89 LWSDHCPOPT_SERVER_ID = 54,
90 LWSDHCPOPT_PARAM_REQ_LIST = 55,
91 LWSDHCPOPT_MESSAGE = 56,
92 LWSDHCPOPT_MAX_DHCP_MSG_SIZE = 57,
93 LWSDHCPOPT_RENEWAL_TIME = 58, /* AKA T1 */
94 LWSDHCPOPT_REBINDING_TIME = 59, /* AKA T2 */
95 LWSDHCPOPT_VENDOR_CLASS_ID = 60,
96 LWSDHCPOPT_CLIENT_ID = 61,
97
98 LWSDHCPOPT_END_OPTIONS = 255
99 };
100
101 typedef struct lws_dhcpc_req {
102 lws_dll2_t list;
103 char domain[64];
104 struct lws_context *context;
105 lws_sorted_usec_list_t sul_conn;
106 lws_sorted_usec_list_t sul_write;
107 dhcpc_cb_t cb; /* cb on completion / failure */
108 void *opaque; /* ignored by lws, give to cb */
109
110 /* these are separated so we can close the bcast one asynchronously */
111 struct lws *wsi_raw; /* for broadcast */
112 lws_dhcpc_state_t state;
113
114 uint32_t ipv4[_IPV4_COUNT];
115
116 uint16_t retry_count_conn;
117 uint16_t retry_count_write;
118 uint8_t mac[6];
119 uint8_t xid[4];
120 uint8_t af; /* address family */
121 } lws_dhcpc_req_t;
122 /* interface name is overallocated here */
123
124 static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ };
125 static const lws_retry_bo_t bo2 = {
126 botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 };
127
128 static const uint8_t rawdisc[] = {
129 0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP,
130 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff,
131 0, 68, 0, 67, 0, 0, 0, 0
132 };
133
134 #define LDHC_OP_BOOTREQUEST 1
135 #define LDHC_OP_BOOTREPLY 2
136
137 /*
138 * IPv4... max total 576
139 *
140 * 0 1 2 3
141 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
142 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
143 * | op (1) | htype (1) | hlen (1) | hops (1) |
144 * +---------------+---------------+---------------+---------------+
145 * | +04 xid (4) |
146 * +-------------------------------+-------------------------------+
147 * | +08 secs (2) | +0a flags (2) |
148 * +-------------------------------+-------------------------------+
149 * | +0C ciaddr (4) client IP |
150 * +---------------------------------------------------------------+
151 * | +10 yiaddr (4) your IP |
152 * +---------------------------------------------------------------+
153 * | +14 siaddr (4) server IP |
154 * +---------------------------------------------------------------+
155 * | +18 giaddr (4) gateway IP |
156 * +---------------------------------------------------------------+
157 * | |
158 * | +1C chaddr (16) client HWADDR |
159 * +---------------------------------------------------------------+
160 * | |
161 * | +2C sname (64) |
162 * +---------------------------------------------------------------+
163 * | |
164 * | +6C file (128) |
165 * +---------------------------------------------------------------+
166 * | |
167 * | +EC options (variable) |
168 * +---------------------------------------------------------------+
169 */
170
171 #if defined(_DEBUG)
172 static const char *dhcp_entry_names[] = {
173 "proposed ip",
174 "dhcp server",
175 "router",
176 "subnet mask",
177 "broadcast",
178 "time server",
179 "dns1",
180 "dns2",
181 "dns3",
182 "dns4",
183 "lease secs",
184 "rebinding secs",
185 "renewal secs",
186 };
187 #endif
188
189 static void
lws_dhcpc_retry_conn(struct lws_sorted_usec_list * sul)190 lws_dhcpc_retry_conn(struct lws_sorted_usec_list *sul)
191 {
192 lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn);
193
194 if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list))
195 return;
196
197 /* create the UDP socket aimed at the server */
198
199 r->retry_count_write = 0;
200 r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0",
201 68, LWS_CAUDP_PF_PACKET |
202 LWS_CAUDP_BROADCAST,
203 "lws-dhcpclient", (const char *)&r[1],
204 NULL, NULL, &bo2);
205 lwsl_debug("%s: created wsi_raw: %p\n", __func__, r->wsi_raw);
206 if (!r->wsi_raw) {
207 lwsl_err("%s: unable to create udp skt\n", __func__);
208
209 lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
210 lws_dhcpc_retry_conn,
211 &r->retry_count_conn);
212
213 return;
214 }
215
216 /* force the network if up */
217 lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0);
218 lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1);
219
220 r->wsi_raw->user_space = r;
221 r->wsi_raw->user_space_externally_allocated = 1;
222
223 lws_get_random(r->wsi_raw->context, r->xid, 4);
224 }
225
226 static void
lws_dhcpc_retry_write(struct lws_sorted_usec_list * sul)227 lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul)
228 {
229 lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_write);
230
231 lwsl_debug("%s\n", __func__);
232
233 if (r && r->wsi_raw)
234 lws_callback_on_writable(r->wsi_raw);
235 }
236
237 #if 0
238 static int
239 lws_sys_dhcpc_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *l,
240 int current, int target)
241 {
242 lws_dhcpc_req_t *r = lws_container_of(l, lws_dhcpc_req_t, notify_link);
243
244 if (target != LWS_SYSTATE_TIME_VALID || v->set_time)
245 return 0;
246
247 /* it's trying to do it ever since the protocol / vhost was set up */
248
249 return 1;
250 }
251 #endif
252
253 static int
lws_dhcpc_prep(uint8_t * start,int bufsiz,lws_dhcpc_req_t * r,int op)254 lws_dhcpc_prep(uint8_t *start, int bufsiz, lws_dhcpc_req_t *r, int op)
255 {
256 uint8_t *p = start;
257
258 memset(start, 0, bufsiz);
259
260 *p++ = 1;
261 *p++ = 1;
262 *p++ = 6; /* sizeof ethernet MAC */
263
264 memcpy(p + 1, r->xid, 4);
265
266 // p[7] = 0x80; /* broadcast flag */
267
268 p += 0x1c - 3;
269
270 if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd,
271 (const char *)&r[1], r->mac, 6) < 0)
272 return -1;
273
274 memcpy(p, r->mac, 6);
275
276 p += 16 + 64 + 128;
277
278 *p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */
279 *p++ = 0x82;
280 *p++ = 0x53;
281 *p++ = 0x63;
282
283 *p++ = LWSDHCPOPT_MESSAGE_TYPE;
284 *p++ = 1; /* length */
285 *p++ = op;
286
287 switch (op) {
288 case LWSDHCPDISCOVER:
289 *p++ = LWSDHCPOPT_PARAM_REQ_LIST;
290 *p++ = 4; /* length */
291 *p++ = 1; /* subnet mask */
292 *p++ = 3; /* router */
293 *p++ = 15; /* domain name */
294 *p++ = 6; /* DNServer */
295 break;
296 case LWSDHCPREQUEST:
297 *p++ = LWSDHCPOPT_REQUESTED_ADS;
298 *p++ = 4; /* length */
299 lws_ser_wu32be(p, r->ipv4[IPV4_PROPOSED]);
300 p += 4;
301 *p++ = LWSDHCPOPT_SERVER_ID;
302 *p++ = 4; /* length */
303 lws_ser_wu32be(p, r->ipv4[IPV4_SERVER]);
304 p += 4;
305 break;
306 }
307
308 *p++ = LWSDHCPOPT_END_OPTIONS;
309
310 return lws_ptr_diff(p, start);
311 }
312
313 static int
callback_dhcpc(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)314 callback_dhcpc(struct lws *wsi, enum lws_callback_reasons reason, void *user,
315 void *in, size_t len)
316 {
317 lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user;
318 uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end;
319 int n, m;
320
321 switch (reason) {
322
323 case LWS_CALLBACK_RAW_ADOPT:
324 lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__);
325 lws_callback_on_writable(wsi);
326 break;
327
328 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
329 lwsl_err("%s: udp conn failed\n", __func__);
330
331 /* fallthru */
332 case LWS_CALLBACK_RAW_CLOSE:
333 lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__);
334 if (!r)
335 break;
336 r->wsi_raw = NULL;
337 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
338 LWS_SET_TIMER_USEC_CANCEL);
339 if (r->state != LDHC_BOUND) {
340 r->state = LDHC_INIT;
341 lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
342 lws_dhcpc_retry_conn,
343 &r->retry_count_conn);
344 }
345 break;
346
347 case LWS_CALLBACK_RAW_RX:
348
349 switch (r->state) {
350 case LDHC_INIT: /* expect DHCPOFFER */
351 case LDHC_REQUESTING: /* expect DHCPACK */
352 /*
353 * We should check carefully if we like what we were
354 * sent... anything can spam us with crafted replies
355 */
356 if (len < 0x100)
357 break;
358
359 p = (uint8_t *)in + 28; /* skip to UDP payload */
360 if (p[0] != 2 || p[1] != 1 || p[2] != 6)
361 break;
362
363 if (memcmp(&p[4], r->xid, 4)) /* must be our xid */
364 break;
365
366 if (memcmp(&p[0x1c], r->mac, 6)) /* our netif mac? */
367 break;
368
369 /* the DHCP magic cookie must be in place */
370 if (lws_ser_ru32be(&p[0xec]) != 0x63825363)
371 break;
372
373 r->ipv4[IPV4_PROPOSED] = lws_ser_ru32be(&p[0x10]);
374 r->ipv4[IPV4_SERVER] = lws_ser_ru32be(&p[0x14]);
375
376 /* it looks legit so far... look at the options */
377
378 end = (uint8_t *)in + len;
379 p += 0xec + 4;
380 while (p < end) {
381 uint8_t c = *p++;
382 uint8_t l = 0;
383
384 if (c && c != 0xff) {
385 /* pad 0 and EOT 0xff have no length */
386 l = *p++;
387 if (!l) {
388 lwsl_err("%s: zero length\n",
389 __func__);
390 goto broken;
391 }
392 if (p + l > end) {
393 /* ...nice try... */
394 lwsl_err("%s: bad len\n",
395 __func__);
396 goto broken;
397 }
398 }
399
400 if (c == 0xff) /* end of options */
401 break;
402
403 m = 0;
404 switch (c) {
405 case LWSDHCPOPT_SUBNET_MASK:
406 n = IPV4_SUBNET_MASK;
407 goto get_ipv4;
408
409 case LWSDHCPOPT_ROUTER:
410 n = IPV4_ROUTER;
411 goto get_ipv4;
412
413 case LWSDHCPOPT_TIME_SERVER:
414 n = IPV4_TIME_SERVER;
415 goto get_ipv4;
416
417 case LWSDHCPOPT_BROADCAST_ADS:
418 n = IPV4_BROADCAST;
419 goto get_ipv4;
420
421 case LWSDHCPOPT_LEASE_TIME:
422 n = IPV4_LEASE_SECS;
423 goto get_ipv4;
424
425 case LWSDHCPOPT_RENEWAL_TIME: /* AKA T1 */
426 n = IPV4_RENEWAL_SECS;
427 goto get_ipv4;
428
429 case LWSDHCPOPT_REBINDING_TIME: /* AKA T2 */
430 n = IPV4_REBINDING_SECS;
431 goto get_ipv4;
432
433 case LWSDHCPOPT_DNSERVER:
434 if (l & 3)
435 break;
436 m = IPV4_DNS_SRV_1;
437 while (l && m - IPV4_DNS_SRV_1 < 4) {
438 r->ipv4[m++] = lws_ser_ru32be(p);
439 l -= 4;
440 p += 4;
441 }
442 break;
443 case LWSDHCPOPT_DOMAIN_NAME:
444 m = l;
445 if (m > (int)sizeof(r->domain) - 1)
446 m = sizeof(r->domain) - 1;
447 memcpy(r->domain, p, m);
448 r->domain[m] = '\0';
449 break;
450
451 case LWSDHCPOPT_MESSAGE_TYPE:
452 /*
453 * Confirm this is the right message
454 * for the state of the negotiation
455 */
456 if (r->state == LDHC_INIT &&
457 *p != LWSDHCPOFFER)
458 goto broken;
459 if (r->state == LDHC_REQUESTING &&
460 *p != LWSDHCPACK)
461 goto broken;
462 break;
463
464 default:
465 break;
466 }
467
468 p += l;
469 continue;
470 get_ipv4:
471 if (l >= 4)
472 r->ipv4[n] = lws_ser_ru32be(p);
473 p += l;
474 continue;
475 broken:
476 memset(r->ipv4, 0, sizeof(r->ipv4));
477 break;
478 }
479
480 #if defined(_DEBUG)
481 /* dump what we have parsed out */
482
483 for (n = 0; n < (int)LWS_ARRAY_SIZE(dhcp_entry_names); n++)
484 if (n >= IPV4_LEASE_SECS)
485 lwsl_info("%s: %s: %ds\n", __func__,
486 dhcp_entry_names[n],
487 r->ipv4[n]);
488 else {
489 m = ntohl(r->ipv4[n]);
490 lws_write_numeric_address((uint8_t *)&m,
491 4,(char *)pkt, 20);
492 lwsl_info("%s: %s: %s\n", __func__,
493 dhcp_entry_names[n],
494 pkt);
495 }
496 #endif
497
498 /*
499 * Having seen everything in there... do we really feel
500 * we could use it? Everything critical is there?
501 */
502
503 if (!r->ipv4[IPV4_PROPOSED] ||
504 !r->ipv4[IPV4_SERVER] ||
505 !r->ipv4[IPV4_ROUTER] ||
506 !r->ipv4[IPV4_SUBNET_MASK] ||
507 !r->ipv4[IPV4_LEASE_SECS] ||
508 !r->ipv4[IPV4_DNS_SRV_1]) {
509 memset(r->ipv4, 0, sizeof(r->ipv4));
510 break;
511 }
512
513 /*
514 * Network layout has to be internally consistent...
515 * DHCP server has to be reachable by broadcast and
516 * default route has to be on same subnet
517 */
518
519 if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
520 (r->ipv4[IPV4_SERVER] & r->ipv4[IPV4_SUBNET_MASK]))
521 break;
522
523 if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
524 (r->ipv4[IPV4_ROUTER] & r->ipv4[IPV4_SUBNET_MASK]))
525 break;
526
527 if (r->state == LDHC_INIT) {
528 lwsl_info("%s: moving to REQ\n", __func__);
529 r->state = LDHC_REQUESTING;
530 lws_callback_on_writable(r->wsi_raw);
531 break;
532 }
533
534 /*
535 * that's it... commit to the configuration
536 */
537
538 /* set up our network interface as offered */
539
540 if (lws_plat_ifconfig_ip((const char *)&r[1],
541 r->wsi_raw->desc.sockfd,
542 (uint8_t *)&r->ipv4[IPV4_PROPOSED],
543 (uint8_t *)&r->ipv4[IPV4_SUBNET_MASK],
544 (uint8_t *)&r->ipv4[IPV4_ROUTER])) {
545 /*
546 * Problem setting the IP... maybe something
547 * transient like racing with NetworkManager?
548 * Since the sul retries are still around it
549 * will retry
550 */
551 return -1;
552 }
553
554 /* clear timeouts related to the broadcast socket */
555
556 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
557 LWS_SET_TIMER_USEC_CANCEL);
558 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
559 LWS_SET_TIMER_USEC_CANCEL);
560
561 lwsl_notice("%s: DHCP configured %s\n", __func__,
562 (const char *)&r[1]);
563 r->state = LDHC_BOUND;
564
565 lws_state_transition_steps(&wsi->context->mgr_system,
566 LWS_SYSTATE_OPERATIONAL);
567
568 r->cb(r->opaque, r->af,
569 (uint8_t *)&r->ipv4[IPV4_PROPOSED], 4);
570
571 r->wsi_raw = NULL;
572 return -1; /* close the broadcast wsi */
573 default:
574 break;
575 }
576
577 break;
578
579 case LWS_CALLBACK_RAW_WRITEABLE:
580
581 if (!r)
582 break;
583
584 /*
585 * UDP is not reliable, it can be locally dropped, or dropped
586 * by any intermediary or the remote peer. So even though we
587 * will do the write in a moment, we schedule another request
588 * for rewrite according to the wsi retry policy.
589 *
590 * If the result came before, we'll cancel it in the close flow.
591 *
592 * If we have already reached the end of our concealed retries
593 * in the policy, just close without another write.
594 */
595 if (lws_dll2_is_detached(&r->sul_write.list) &&
596 lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write,
597 lws_dhcpc_retry_write,
598 &r->retry_count_write)) {
599 /* we have reached the end of our concealed retries */
600 lwsl_warn("%s: concealed retries done, failing\n",
601 __func__);
602 goto retry_conn;
603 }
604
605 switch (r->state) {
606 case LDHC_INIT:
607 n = LWSDHCPDISCOVER;
608 goto bcast;
609
610 case LDHC_REQUESTING:
611 n = LWSDHCPREQUEST;
612
613 /* fallthru */
614 bcast:
615 n = lws_dhcpc_prep(p + 28, sizeof(pkt) - LWS_PRE - 28,
616 r, n);
617 if (n < 0) {
618 lwsl_err("%s: failed to prep\n", __func__);
619 break;
620 }
621
622 m = lws_plat_rawudp_broadcast(p, rawdisc,
623 LWS_ARRAY_SIZE(rawdisc),
624 n + 28,
625 r->wsi_raw->desc.sockfd,
626 (const char *)&r[1]);
627 if (m < 0)
628 lwsl_err("%s: Failed to write dhcp client req: "
629 "%d %d, errno %d\n", __func__,
630 n, m, LWS_ERRNO);
631 break;
632 default:
633 break;
634 }
635
636 return 0;
637
638 retry_conn:
639 lws_retry_sul_schedule(wsi->context, 0, &r->sul_conn, &bo2,
640 lws_dhcpc_retry_conn,
641 &r->retry_count_conn);
642
643 return -1;
644
645 default:
646 break;
647 }
648
649 return 0;
650
651 #if 0
652 cancel_conn_timer:
653 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
654 LWS_SET_TIMER_USEC_CANCEL);
655
656 return 0;
657 #endif
658 }
659
660 struct lws_protocols lws_system_protocol_dhcpc =
661 { "lws-dhcpclient", callback_dhcpc, 0, 128, };
662
663 static void
lws_dhcpc_destroy(lws_dhcpc_req_t ** pr)664 lws_dhcpc_destroy(lws_dhcpc_req_t **pr)
665 {
666 lws_dhcpc_req_t *r = *pr;
667
668 lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
669 LWS_SET_TIMER_USEC_CANCEL);
670 lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
671 LWS_SET_TIMER_USEC_CANCEL);
672 if (r->wsi_raw)
673 lws_set_timeout(r->wsi_raw, 1, LWS_TO_KILL_ASYNC);
674
675 lws_dll2_remove(&r->list);
676
677 lws_free_set_NULL(r);
678 }
679
680 int
lws_dhcpc_status(struct lws_context * context,lws_sockaddr46 * sa46)681 lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46)
682 {
683 lws_dhcpc_req_t *r;
684
685 lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
686 r = (lws_dhcpc_req_t *)p;
687
688 if (r->state == LDHC_BOUND) {
689 if (sa46) {
690 memset(sa46, 0, sizeof(*sa46));
691 sa46->sa4.sin_family = AF_INET;
692 sa46->sa4.sin_addr.s_addr = r->ipv4[IPV4_DNS_SRV_1];
693 }
694 return 1;
695 }
696
697 } lws_end_foreach_dll(p);
698
699 return 0;
700 }
701
702 static lws_dhcpc_req_t *
lws_dhcpc_find(struct lws_context * context,const char * iface,int af)703 lws_dhcpc_find(struct lws_context *context, const char *iface, int af)
704 {
705 lws_dhcpc_req_t *r;
706
707 /* see if we are already looking after this af / iface combination */
708
709 lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
710 r = (lws_dhcpc_req_t *)p;
711
712 if (!strcmp((const char *)&r[1], iface) && af == r->af)
713 return r; /* yes... */
714
715 } lws_end_foreach_dll(p);
716
717 return NULL;
718 }
719
720 /*
721 * Create a persistent dhcp client entry for network interface "iface" and AF
722 * type "af"
723 */
724
725 int
lws_dhcpc_request(struct lws_context * context,const char * iface,int af,dhcpc_cb_t cb,void * opaque)726 lws_dhcpc_request(struct lws_context *context, const char *iface, int af,
727 dhcpc_cb_t cb, void *opaque)
728 {
729 lws_dhcpc_req_t *r = lws_dhcpc_find(context, iface, af);
730 int n;
731
732 /* see if we are already looking after this af / iface combination */
733
734 if (r)
735 return 0;
736
737 /* nope... let's create a request object as he asks */
738
739 n = strlen(iface);
740 r = lws_zalloc(sizeof(*r) + n + 1, __func__);
741 if (!r)
742 return 1;
743
744 memcpy(&r[1], iface, n + 1);
745 r->af = af;
746 r->cb = cb;
747 r->opaque = opaque;
748 r->context = context;
749 r->state = LDHC_INIT;
750
751 lws_dll2_add_head(&r->list, &context->dhcpc_owner); /* add him to list */
752
753 lws_dhcpc_retry_conn(&r->sul_conn);
754
755 return 0;
756 }
757
758 /*
759 * Destroy every DHCP client object related to interface "iface"
760 */
761
762 static int
_remove_if(struct lws_dll2 * d,void * opaque)763 _remove_if(struct lws_dll2 *d, void *opaque)
764 {
765 lws_dhcpc_req_t *r = lws_container_of(d, lws_dhcpc_req_t, list);
766
767 if (!opaque || !strcmp((const char *)&r[1], (const char *)opaque))
768 lws_dhcpc_destroy(&r);
769
770 return 0;
771 }
772
773 int
lws_dhcpc_remove(struct lws_context * context,const char * iface)774 lws_dhcpc_remove(struct lws_context *context, const char *iface)
775 {
776 lws_dll2_foreach_safe(&context->dhcpc_owner, (void *)iface, _remove_if);
777
778 return 0;
779 }
780