• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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