• 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 static const char *dhcp_entry_names[] = {
172 	"proposed ip",
173 	"dhcp server",
174 	"router",
175 	"subnet mask",
176 	"broadcast",
177 	"time server",
178 	"dns1",
179 	"dns2",
180 	"dns3",
181 	"dns4",
182 	"lease secs",
183 	"rebinding secs",
184 	"renewal secs",
185 };
186 
187 static void
lws_dhcpc_retry_conn(struct lws_sorted_usec_list * sul)188 lws_dhcpc_retry_conn(struct lws_sorted_usec_list *sul)
189 {
190 	lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn);
191 
192 	if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list))
193 		return;
194 
195 	/* create the UDP socket aimed at the server */
196 
197 	r->retry_count_write = 0;
198 	r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0",
199 					  68, LWS_CAUDP_PF_PACKET |
200 					      LWS_CAUDP_BROADCAST,
201 					  "lws-dhcpclient", (const char *)&r[1],
202 					  NULL, NULL, &bo2);
203 	lwsl_debug("%s: created wsi_raw: %p\n", __func__, r->wsi_raw);
204 	if (!r->wsi_raw) {
205 		lwsl_err("%s: unable to create udp skt\n", __func__);
206 
207 		lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
208 				       lws_dhcpc_retry_conn,
209 				       &r->retry_count_conn);
210 
211 		return;
212 	}
213 
214 	/* force the network if up */
215 	lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0);
216 	lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1);
217 
218 	r->wsi_raw->user_space = r;
219 	r->wsi_raw->user_space_externally_allocated = 1;
220 
221 	lws_get_random(r->wsi_raw->context, r->xid, 4);
222 }
223 
224 static void
lws_dhcpc_retry_write(struct lws_sorted_usec_list * sul)225 lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul)
226 {
227 	lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_write);
228 
229 	lwsl_debug("%s\n", __func__);
230 
231 	if (r && r->wsi_raw)
232 		lws_callback_on_writable(r->wsi_raw);
233 }
234 
235 #if 0
236 static int
237 lws_sys_dhcpc_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *l,
238 		        int current, int target)
239 {
240 	lws_dhcpc_req_t *r = lws_container_of(l, lws_dhcpc_req_t, notify_link);
241 
242 	if (target != LWS_SYSTATE_TIME_VALID || v->set_time)
243 		return 0;
244 
245 	/* it's trying to do it ever since the protocol / vhost was set up */
246 
247 	return 1;
248 }
249 #endif
250 
251 static int
lws_dhcpc_prep(uint8_t * start,int bufsiz,lws_dhcpc_req_t * r,int op)252 lws_dhcpc_prep(uint8_t *start, int bufsiz, lws_dhcpc_req_t *r, int op)
253 {
254 	uint8_t *p = start;
255 
256 	memset(start, 0, bufsiz);
257 
258 	*p++ = 1;
259 	*p++ = 1;
260 	*p++ = 6; /* sizeof ethernet MAC */
261 
262 	memcpy(p + 1, r->xid, 4);
263 
264 //	p[7] = 0x80; /* broadcast flag */
265 
266 	p += 0x1c - 3;
267 
268 	if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd,
269 				      (const char *)&r[1], r->mac, 6) < 0)
270 		return -1;
271 
272 	memcpy(p, r->mac, 6);
273 
274 	p += 16 + 64 + 128;
275 
276 	*p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */
277 	*p++ = 0x82;
278 	*p++ = 0x53;
279 	*p++ = 0x63;
280 
281 	*p++ = LWSDHCPOPT_MESSAGE_TYPE;
282 	*p++ = 1;	/* length */
283 	*p++ = op;
284 
285 	switch (op) {
286 	case LWSDHCPDISCOVER:
287 		*p++ = LWSDHCPOPT_PARAM_REQ_LIST;
288 		*p++ = 4; 	/* length */
289 		*p++ = 1;	/* subnet mask */
290 		*p++ = 3;	/* router */
291 		*p++ = 15;	/* domain name */
292 		*p++ = 6;	/* DNServer */
293 		break;
294 	case LWSDHCPREQUEST:
295 		*p++ = LWSDHCPOPT_REQUESTED_ADS;
296 		*p++ = 4; 	/* length */
297 		lws_ser_wu32be(p, r->ipv4[IPV4_PROPOSED]);
298 		p += 4;
299 		*p++ = LWSDHCPOPT_SERVER_ID;
300 		*p++ = 4; 	/* length */
301 		lws_ser_wu32be(p, r->ipv4[IPV4_SERVER]);
302 		p += 4;
303 		break;
304 	}
305 
306 	*p++ = LWSDHCPOPT_END_OPTIONS;
307 
308 	return lws_ptr_diff(p, start);
309 }
310 
311 static int
callback_dhcpc(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)312 callback_dhcpc(struct lws *wsi, enum lws_callback_reasons reason, void *user,
313 	       void *in, size_t len)
314 {
315 	lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user;
316 	uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end;
317 	int n, m;
318 
319 	switch (reason) {
320 
321         case LWS_CALLBACK_RAW_ADOPT:
322 		lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__);
323 		lws_callback_on_writable(wsi);
324 		break;
325 
326 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
327 		lwsl_err("%s: udp conn failed\n", __func__);
328 
329 		/* fallthru */
330 	case LWS_CALLBACK_RAW_CLOSE:
331 		lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__);
332 		if (!r)
333 			break;
334 		r->wsi_raw = NULL;
335 		lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
336 				 LWS_SET_TIMER_USEC_CANCEL);
337 		if (r->state != LDHC_BOUND) {
338 			r->state = LDHC_INIT;
339 			lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
340 					       lws_dhcpc_retry_conn,
341 					       &r->retry_count_conn);
342 		}
343 		break;
344 
345 	case LWS_CALLBACK_RAW_RX:
346 
347 		switch (r->state) {
348 		case LDHC_INIT:		/* expect DHCPOFFER */
349 		case LDHC_REQUESTING:	/* expect DHCPACK */
350 			/*
351 			 * We should check carefully if we like what we were
352 			 * sent... anything can spam us with crafted replies
353 			 */
354 			if (len < 0x100)
355 				break;
356 
357 			p = (uint8_t *)in + 28; /* skip to UDP payload */
358 			if (p[0] != 2 || p[1] != 1 || p[2] != 6)
359 				break;
360 
361 			if (memcmp(&p[4], r->xid, 4))	/* must be our xid */
362 				break;
363 
364 			if (memcmp(&p[0x1c], r->mac, 6)) /* our netif mac? */
365 				break;
366 
367 			/* the DHCP magic cookie must be in place */
368 			if (lws_ser_ru32be(&p[0xec]) != 0x63825363)
369 				break;
370 
371 			r->ipv4[IPV4_PROPOSED] = lws_ser_ru32be(&p[0x10]);
372 			r->ipv4[IPV4_SERVER] = lws_ser_ru32be(&p[0x14]);
373 
374 			/* it looks legit so far... look at the options */
375 
376 			end = (uint8_t *)in + len;
377 			p += 0xec + 4;
378 			while (p < end) {
379 				uint8_t c = *p++;
380 				uint8_t l = 0;
381 
382 				if (c && c != 0xff) {
383 					/* pad 0 and EOT 0xff have no length */
384 					l = *p++;
385 					if (!l) {
386 						lwsl_err("%s: zero length\n",
387 								__func__);
388 						goto broken;
389 					}
390 					if (p + l > end) {
391 						/* ...nice try... */
392 						lwsl_err("%s: bad len\n",
393 								__func__);
394 						goto broken;
395 					}
396 				}
397 
398 				if (c == 0xff) /* end of options */
399 					break;
400 
401 				m = 0;
402 				switch (c) {
403 				case LWSDHCPOPT_SUBNET_MASK:
404 					n = IPV4_SUBNET_MASK;
405 					goto get_ipv4;
406 
407 				case LWSDHCPOPT_ROUTER:
408 					n = IPV4_ROUTER;
409 					goto get_ipv4;
410 
411 				case LWSDHCPOPT_TIME_SERVER:
412 					n = IPV4_TIME_SERVER;
413 					goto get_ipv4;
414 
415 				case LWSDHCPOPT_BROADCAST_ADS:
416 					n = IPV4_BROADCAST;
417 					goto get_ipv4;
418 
419 				case LWSDHCPOPT_LEASE_TIME:
420 					n = IPV4_LEASE_SECS;
421 					goto get_ipv4;
422 
423 				case LWSDHCPOPT_RENEWAL_TIME: /* AKA T1 */
424 					n = IPV4_RENEWAL_SECS;
425 					goto get_ipv4;
426 
427 				case LWSDHCPOPT_REBINDING_TIME: /* AKA T2 */
428 					n = IPV4_REBINDING_SECS;
429 					goto get_ipv4;
430 
431 				case LWSDHCPOPT_DNSERVER:
432 					if (l & 3)
433 						break;
434 					m = IPV4_DNS_SRV_1;
435 					while (l && m - IPV4_DNS_SRV_1 < 4) {
436 						r->ipv4[m++] = lws_ser_ru32be(p);
437 						l -= 4;
438 						p += 4;
439 					}
440 					break;
441 				case LWSDHCPOPT_DOMAIN_NAME:
442 					m = l;
443 					if (m > (int)sizeof(r->domain) - 1)
444 						m = sizeof(r->domain) - 1;
445 					memcpy(r->domain, p, m);
446 					r->domain[m] = '\0';
447 					break;
448 
449 				case LWSDHCPOPT_MESSAGE_TYPE:
450 					/*
451 					 * Confirm this is the right message
452 					 * for the state of the negotiation
453 					 */
454 					if (r->state == LDHC_INIT &&
455 					    *p != LWSDHCPOFFER)
456 						goto broken;
457 					if (r->state == LDHC_REQUESTING &&
458 					    *p != LWSDHCPACK)
459 						goto broken;
460 					break;
461 
462 				default:
463 					break;
464 				}
465 
466 				p += l;
467 				continue;
468 get_ipv4:
469 				if (l >= 4)
470 					r->ipv4[n] = lws_ser_ru32be(p);
471 				p += l;
472 				continue;
473 broken:
474 				memset(r->ipv4, 0, sizeof(r->ipv4));
475 				break;
476 			}
477 
478 #if defined(_DEBUG)
479 			/* dump what we have parsed out */
480 
481 			for (n = 0; n < (int)LWS_ARRAY_SIZE(dhcp_entry_names); n++)
482 				if (n >= IPV4_LEASE_SECS)
483 					lwsl_info("%s: %s: %ds\n", __func__,
484 						    dhcp_entry_names[n],
485 						    r->ipv4[n]);
486 				else {
487 					m = ntohl(r->ipv4[n]);
488 					lws_write_numeric_address((uint8_t *)&m,
489 							     4,(char *)pkt, 20);
490 					lwsl_info("%s: %s: %s\n", __func__,
491 							dhcp_entry_names[n],
492 							pkt);
493 				}
494 #endif
495 
496 			/*
497 			 * Having seen everything in there... do we really feel
498 			 * we could use it?  Everything critical is there?
499 			 */
500 
501 			if (!r->ipv4[IPV4_PROPOSED] ||
502 			    !r->ipv4[IPV4_SERVER] ||
503 			    !r->ipv4[IPV4_ROUTER] ||
504 			    !r->ipv4[IPV4_SUBNET_MASK] ||
505 			    !r->ipv4[IPV4_LEASE_SECS] ||
506 			    !r->ipv4[IPV4_DNS_SRV_1]) {
507 				memset(r->ipv4, 0, sizeof(r->ipv4));
508 				break;
509 			}
510 
511 			/*
512 			 * Network layout has to be internally consistent...
513 			 * DHCP server has to be reachable by broadcast and
514 			 * default route has to be on same subnet
515 			 */
516 
517 			if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
518 			    (r->ipv4[IPV4_SERVER] & r->ipv4[IPV4_SUBNET_MASK]))
519 				break;
520 
521 			if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) !=
522 			    (r->ipv4[IPV4_ROUTER] & r->ipv4[IPV4_SUBNET_MASK]))
523 				break;
524 
525 			if (r->state == LDHC_INIT) {
526 				lwsl_info("%s: moving to REQ\n", __func__);
527 				r->state = LDHC_REQUESTING;
528 				lws_callback_on_writable(r->wsi_raw);
529 				break;
530 			}
531 
532 			/*
533 			 * that's it... commit to the configuration
534 			 */
535 
536 			/* set up our network interface as offered */
537 
538 			if (lws_plat_ifconfig_ip((const char *)&r[1],
539 						 r->wsi_raw->desc.sockfd,
540 					(uint8_t *)&r->ipv4[IPV4_PROPOSED],
541 					(uint8_t *)&r->ipv4[IPV4_SUBNET_MASK],
542 					(uint8_t *)&r->ipv4[IPV4_ROUTER])) {
543 				/*
544 				 * Problem setting the IP... maybe something
545 				 * transient like racing with NetworkManager?
546 				 * Since the sul retries are still around it
547 				 * will retry
548 				 */
549 				return -1;
550 			}
551 
552 			/* clear timeouts related to the broadcast socket */
553 
554 			lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
555 					 LWS_SET_TIMER_USEC_CANCEL);
556 			lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
557 					 LWS_SET_TIMER_USEC_CANCEL);
558 
559 			lwsl_notice("%s: DHCP configured %s\n", __func__,
560 					(const char *)&r[1]);
561 			r->state = LDHC_BOUND;
562 
563 			lws_state_transition_steps(&wsi->context->mgr_system,
564 						   LWS_SYSTATE_OPERATIONAL);
565 
566 			r->cb(r->opaque, r->af,
567 					(uint8_t *)&r->ipv4[IPV4_PROPOSED], 4);
568 
569 			r->wsi_raw = NULL;
570 			return -1; /* close the broadcast wsi */
571 		default:
572 			break;
573 		}
574 
575 		break;
576 
577 	case LWS_CALLBACK_RAW_WRITEABLE:
578 
579 		if (!r)
580 			break;
581 
582 		/*
583 		 * UDP is not reliable, it can be locally dropped, or dropped
584 		 * by any intermediary or the remote peer.  So even though we
585 		 * will do the write in a moment, we schedule another request
586 		 * for rewrite according to the wsi retry policy.
587 		 *
588 		 * If the result came before, we'll cancel it in the close flow.
589 		 *
590 		 * If we have already reached the end of our concealed retries
591 		 * in the policy, just close without another write.
592 		 */
593 		if (lws_dll2_is_detached(&r->sul_write.list) &&
594 		    lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write,
595 						     lws_dhcpc_retry_write,
596 						     &r->retry_count_write)) {
597 			/* we have reached the end of our concealed retries */
598 			lwsl_warn("%s: concealed retries done, failing\n",
599 				  __func__);
600 			goto retry_conn;
601 		}
602 
603 		switch (r->state) {
604 		case LDHC_INIT:
605 			n = LWSDHCPDISCOVER;
606 			goto bcast;
607 
608 		case LDHC_REQUESTING:
609 			n = LWSDHCPREQUEST;
610 
611 			/* fallthru */
612 bcast:
613 			n = lws_dhcpc_prep(p + 28, sizeof(pkt) - LWS_PRE - 28,
614 					   r, n);
615 			if (n < 0) {
616 				lwsl_err("%s: failed to prep\n", __func__);
617 				break;
618 			}
619 
620 			m = lws_plat_rawudp_broadcast(p, rawdisc,
621 						      LWS_ARRAY_SIZE(rawdisc),
622 						      n + 28,
623 						      r->wsi_raw->desc.sockfd,
624 						      (const char *)&r[1]);
625 			if (m < 0)
626 				lwsl_err("%s: Failed to write dhcp client req: "
627 					 "%d %d, errno %d\n", __func__,
628 					 n, m, LWS_ERRNO);
629 			break;
630 		default:
631 			break;
632 		}
633 
634 		return 0;
635 
636 retry_conn:
637 		lws_retry_sul_schedule(wsi->context, 0, &r->sul_conn, &bo2,
638 				       lws_dhcpc_retry_conn,
639 				       &r->retry_count_conn);
640 
641 		return -1;
642 
643 	default:
644 		break;
645 	}
646 
647 	return 0;
648 
649 #if 0
650 cancel_conn_timer:
651 	lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
652 			 LWS_SET_TIMER_USEC_CANCEL);
653 
654 	return 0;
655 #endif
656 }
657 
658 struct lws_protocols lws_system_protocol_dhcpc =
659 	{ "lws-dhcpclient", callback_dhcpc, 0, 128, };
660 
661 static void
lws_dhcpc_destroy(lws_dhcpc_req_t ** pr)662 lws_dhcpc_destroy(lws_dhcpc_req_t **pr)
663 {
664 	lws_dhcpc_req_t *r = *pr;
665 
666 	lws_sul_schedule(r->context, 0, &r->sul_conn, NULL,
667 			 LWS_SET_TIMER_USEC_CANCEL);
668 	lws_sul_schedule(r->context, 0, &r->sul_write, NULL,
669 			 LWS_SET_TIMER_USEC_CANCEL);
670 	if (r->wsi_raw)
671 		lws_set_timeout(r->wsi_raw, 1, LWS_TO_KILL_ASYNC);
672 
673 	lws_dll2_remove(&r->list);
674 
675 	lws_free_set_NULL(r);
676 }
677 
678 int
lws_dhcpc_status(struct lws_context * context,lws_sockaddr46 * sa46)679 lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46)
680 {
681 	lws_dhcpc_req_t *r;
682 
683 	lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
684 		r = (lws_dhcpc_req_t *)p;
685 
686 		if (r->state == LDHC_BOUND) {
687 			if (sa46) {
688 				memset(sa46, 0, sizeof(*sa46));
689 				sa46->sa4.sin_family = AF_INET;
690 				sa46->sa4.sin_addr.s_addr = r->ipv4[IPV4_DNS_SRV_1];
691 			}
692 			return 1;
693 		}
694 
695 	} lws_end_foreach_dll(p);
696 
697 	return 0;
698 }
699 
700 static lws_dhcpc_req_t *
lws_dhcpc_find(struct lws_context * context,const char * iface,int af)701 lws_dhcpc_find(struct lws_context *context, const char *iface, int af)
702 {
703 	lws_dhcpc_req_t *r;
704 
705 	/* see if we are already looking after this af / iface combination */
706 
707 	lws_start_foreach_dll(struct lws_dll2 *, p, context->dhcpc_owner.head) {
708 		r = (lws_dhcpc_req_t *)p;
709 
710 		if (!strcmp((const char *)&r[1], iface) && af == r->af)
711 			return r; /* yes...  */
712 
713 	} lws_end_foreach_dll(p);
714 
715 	return NULL;
716 }
717 
718 /*
719  * Create a persistent dhcp client entry for network interface "iface" and AF
720  * type "af"
721  */
722 
723 int
lws_dhcpc_request(struct lws_context * context,const char * iface,int af,dhcpc_cb_t cb,void * opaque)724 lws_dhcpc_request(struct lws_context *context, const char *iface, int af,
725 		  dhcpc_cb_t cb, void *opaque)
726 {
727 	lws_dhcpc_req_t *r = lws_dhcpc_find(context, iface, af);
728 	int n;
729 
730 	/* see if we are already looking after this af / iface combination */
731 
732 	if (r)
733 		return 0;
734 
735 	/* nope... let's create a request object as he asks */
736 
737 	n = strlen(iface);
738 	r = lws_zalloc(sizeof(*r) + n + 1, __func__);
739 	if (!r)
740 		return 1;
741 
742 	memcpy(&r[1], iface, n + 1);
743 	r->af = af;
744 	r->cb = cb;
745 	r->opaque = opaque;
746 	r->context = context;
747 	r->state = LDHC_INIT;
748 
749 	lws_dll2_add_head(&r->list, &context->dhcpc_owner); /* add him to list */
750 
751 	lws_dhcpc_retry_conn(&r->sul_conn);
752 
753 	return 0;
754 }
755 
756 /*
757  * Destroy every DHCP client object related to interface "iface"
758  */
759 
760 static int
_remove_if(struct lws_dll2 * d,void * opaque)761 _remove_if(struct lws_dll2 *d, void *opaque)
762 {
763 	lws_dhcpc_req_t *r = lws_container_of(d, lws_dhcpc_req_t, list);
764 
765 	if (!opaque || !strcmp((const char *)&r[1], (const char *)opaque))
766 		lws_dhcpc_destroy(&r);
767 
768 	return 0;
769 }
770 
771 int
lws_dhcpc_remove(struct lws_context * context,const char * iface)772 lws_dhcpc_remove(struct lws_context *context, const char *iface)
773 {
774 	lws_dll2_foreach_safe(&context->dhcpc_owner, (void *)iface, _remove_if);
775 
776 	return 0;
777 }
778