• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2021 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  * The protocol part of dhcp4 client
25  */
26 
27 #include "private-lib-core.h"
28 #include "private-lib-system-dhcpclient.h"
29 
30 #define LDHC_OP_BOOTREQUEST 1
31 #define LDHC_OP_BOOTREPLY 2
32 
33 /*
34  *  IPv4... max total 576
35  *
36  * 0                   1                   2                   3
37  * 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
38  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39  * |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
40  * +---------------+---------------+---------------+---------------+
41  * |  +04                       xid (4)                            |
42  * +-------------------------------+-------------------------------+
43  * |  +08      secs (2)            |  +0a         flags (2)        |
44  * +-------------------------------+-------------------------------+
45  * |  +0C                     ciaddr  (4)      client IP           |
46  * +---------------------------------------------------------------+
47  * |  +10                     yiaddr  (4)      your IP             |
48  * +---------------------------------------------------------------+
49  * |  +14                     siaddr  (4)      server IP           |
50  * +---------------------------------------------------------------+
51  * |  +18                     giaddr  (4)      gateway IP          |
52  * +---------------------------------------------------------------+
53  * |                                                               |
54  * |  +1C                     chaddr  (16)     client HWADDR       |
55  * +---------------------------------------------------------------+
56  * |                                                               |
57  * |  +2C                     sname   (64)                         |
58  * +---------------------------------------------------------------+
59  * |                                                               |
60  * |  +6C                     file    (128)                        |
61  * +---------------------------------------------------------------+
62  * |                                                               |
63  * |  +EC                     options (variable)                   |
64  * +---------------------------------------------------------------+
65  */
66 
67 static const uint8_t rawdisc4[] = {
68 	0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP,
69 	0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff,
70 	0, 68, 0, 67, 0, 0, 0, 0
71 };
72 
73 static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ };
74 static const lws_retry_bo_t bo2 = {
75 	botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 };
76 
77 static int
lws_dhcpc4_prep(uint8_t * start,unsigned int bufsiz,lws_dhcpc_req_t * r,int op)78 lws_dhcpc4_prep(uint8_t *start, unsigned int bufsiz, lws_dhcpc_req_t *r, int op)
79 {
80 	uint8_t *p = start;
81 
82 	memset(start, 0, bufsiz);
83 
84 	*p++ = 1;
85 	*p++ = 1;
86 	*p++ = 6; /* sizeof ethernet MAC */
87 
88 	memcpy(p + 1, r->xid, 4);
89 
90 //	p[7] = 0x80; /* broadcast flag */
91 
92 	p += 0x1c - 3;
93 
94 	if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd,
95 				      (const char *)&r[1], r->is.mac, 6) < 0)
96 		return -1;
97 
98 	memcpy(p, r->is.mac, 6);
99 
100 	p += 16 + 64 + 128;
101 
102 	*p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */
103 	*p++ = 0x82;
104 	*p++ = 0x53;
105 	*p++ = 0x63;
106 
107 	*p++ = LWSDHC4POPT_MESSAGE_TYPE;
108 	*p++ = 1;	/* length */
109 	*p++ = (uint8_t)op;
110 
111 	switch (op) {
112 	case LWSDHC4PDISCOVER:
113 		*p++ = LWSDHC4POPT_PARAM_REQ_LIST;
114 		*p++ = 4; 	/* length */
115 		*p++ = LWSDHC4POPT_SUBNET_MASK;
116 		*p++ = LWSDHC4POPT_ROUTER;
117 		*p++ = LWSDHC4POPT_DNSERVER;
118 		*p++ = LWSDHC4POPT_DOMAIN_NAME;
119 		break;
120 
121 	case LWSDHC4PREQUEST:
122 		if (r->is.sa46[LWSDH_SA46_IP].sa4.sin_family != AF_INET)
123 			break;
124 		*p++ = LWSDHC4POPT_REQUESTED_ADS;
125 		*p++ = 4; 	/* length */
126 		lws_ser_wu32be(p, r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr);
127 		p += 4;
128 		*p++ = LWSDHC4POPT_SERVER_ID;
129 		*p++ = 4; 	/* length */
130 		lws_ser_wu32be(p, r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_addr.s_addr);
131 		p += 4;
132 		break;
133 	}
134 
135 	*p++ = LWSDHC4POPT_END_OPTIONS;
136 
137 	return lws_ptr_diff(p, start);
138 }
139 
140 static int
callback_dhcpc4(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)141 callback_dhcpc4(struct lws *wsi, enum lws_callback_reasons reason, void *user,
142 	       void *in, size_t len)
143 {
144 	lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user;
145 	uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE;
146 	int n, m;
147 
148 	switch (reason) {
149 
150         case LWS_CALLBACK_RAW_ADOPT:
151 		lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__);
152 		lws_callback_on_writable(wsi);
153 		break;
154 
155 	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
156 		lwsl_err("%s: udp conn failed\n", __func__);
157 
158 		/* fallthru */
159 	case LWS_CALLBACK_RAW_CLOSE:
160 		lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__);
161 		if (!r)
162 			break;
163 		r->wsi_raw = NULL;
164 		lws_sul_cancel(&r->sul_write);
165 		if (r->state != LDHC_BOUND) {
166 			r->state = LDHC_INIT;
167 			lws_retry_sul_schedule(r->context, 0, &r->sul_conn,
168 					       &bo2, lws_dhcpc4_retry_conn,
169 					       &r->retry_count_conn);
170 		}
171 		break;
172 
173 	case LWS_CALLBACK_RAW_RX:
174 
175 		if (lws_dhcpc4_parse(r, in, len))
176 			break;
177 
178 		/*
179 		 * that's it... commit to the configuration
180 		 */
181 
182 		/* set up our network interface as offered */
183 
184 		if (lws_plat_ifconfig(r->wsi_raw->desc.sockfd, &r->is))
185 			/*
186 			 * Problem setting the IP... maybe something
187 			 * transient like racing with NetworkManager?
188 			 * Since the sul retries are still around it
189 			 * will retry
190 			 */
191 			return -1;
192 
193 		/* clear timeouts related to the broadcast socket */
194 
195 		lws_sul_cancel(&r->sul_write);
196 		lws_sul_cancel(&r->sul_conn);
197 
198 		lwsl_notice("%s: DHCP configured %s\n", __func__,
199 				(const char *)&r[1]);
200 		r->state = LDHC_BOUND;
201 
202 		lws_state_transition_steps(&wsi->a.context->mgr_system,
203 					   LWS_SYSTATE_OPERATIONAL);
204 
205 		r->cb(r->opaque, &r->is);
206 
207 		r->wsi_raw = NULL;
208 
209 		return -1; /* close the broadcast wsi */
210 
211 	case LWS_CALLBACK_RAW_WRITEABLE:
212 
213 		if (!r)
214 			break;
215 
216 		/*
217 		 * UDP is not reliable, it can be locally dropped, or dropped
218 		 * by any intermediary or the remote peer.  So even though we
219 		 * will do the write in a moment, we schedule another request
220 		 * for rewrite according to the wsi retry policy.
221 		 *
222 		 * If the result came before, we'll cancel it in the close flow.
223 		 *
224 		 * If we have already reached the end of our concealed retries
225 		 * in the policy, just close without another write.
226 		 */
227 		if (lws_dll2_is_detached(&r->sul_write.list) &&
228 		    lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write,
229 						     lws_dhcpc_retry_write,
230 						     &r->retry_count_write)) {
231 			/* we have reached the end of our concealed retries */
232 			lwsl_warn("%s: concealed retries done, failing\n",
233 				  __func__);
234 			goto retry_conn;
235 		}
236 
237 		switch (r->state) {
238 		case LDHC_INIT:
239 			n = LWSDHC4PDISCOVER;
240 			goto bcast;
241 
242 		case LDHC_REQUESTING:
243 			n = LWSDHC4PREQUEST;
244 
245 			/* fallthru */
246 bcast:
247 			n = lws_dhcpc4_prep(p + 28, (unsigned int)
248 					(sizeof(pkt) - LWS_PRE - 28), r, n);
249 			if (n < 0) {
250 				lwsl_err("%s: failed to prep\n", __func__);
251 				break;
252 			}
253 
254 			m = lws_plat_rawudp_broadcast(p, rawdisc4,
255 						      LWS_ARRAY_SIZE(rawdisc4),
256 						      (size_t)(n + 28),
257 						      r->wsi_raw->desc.sockfd,
258 						      (const char *)&r[1]);
259 			if (m < 0)
260 				lwsl_err("%s: Failed to write dhcp client req: "
261 					 "%d %d, errno %d\n", __func__,
262 					 n, m, LWS_ERRNO);
263 			break;
264 		default:
265 			break;
266 		}
267 
268 		return 0;
269 
270 retry_conn:
271 		lws_retry_sul_schedule(wsi->a.context, 0, &r->sul_conn, &bo2,
272 				       lws_dhcpc4_retry_conn,
273 				       &r->retry_count_conn);
274 
275 		return -1;
276 
277 	default:
278 		break;
279 	}
280 
281 	return 0;
282 }
283 
284 struct lws_protocols lws_system_protocol_dhcpc4 =
285 	{ "lws-dhcp4client", callback_dhcpc4, 0, 128, 0, NULL, 0 };
286 
287 void
lws_dhcpc4_retry_conn(struct lws_sorted_usec_list * sul)288 lws_dhcpc4_retry_conn(struct lws_sorted_usec_list *sul)
289 {
290 	lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn);
291 
292 	if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list))
293 		return;
294 
295 	/* create the UDP socket aimed at the server */
296 
297 	r->retry_count_write = 0;
298 	r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0",
299 					  68, LWS_CAUDP_PF_PACKET |
300 					      LWS_CAUDP_BROADCAST,
301 					  "lws-dhcp4client", (const char *)&r[1],
302 					  NULL, NULL, &bo2, "dhcpc");
303 	lwsl_debug("%s: created wsi_raw: %s\n", __func__, lws_wsi_tag(r->wsi_raw));
304 	if (!r->wsi_raw) {
305 		lwsl_err("%s: unable to create udp skt\n", __func__);
306 
307 		lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2,
308 				       lws_dhcpc4_retry_conn,
309 				       &r->retry_count_conn);
310 
311 		return;
312 	}
313 
314 	/* force the network if up */
315 	lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0);
316 	lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1);
317 
318 	r->wsi_raw->user_space = r;
319 	r->wsi_raw->user_space_externally_allocated = 1;
320 
321 	lws_get_random(r->wsi_raw->a.context, r->xid, 4);
322 }
323 
324 static void
lws_sa46_set_ipv4(lws_dhcpc_req_t * r,unsigned int which,uint8_t * p)325 lws_sa46_set_ipv4(lws_dhcpc_req_t *r, unsigned int which, uint8_t *p)
326 {
327 	r->is.sa46[which].sa4.sin_family = AF_INET;
328 	r->is.sa46[which].sa4.sin_addr.s_addr = ntohl(lws_ser_ru32be(p));
329 }
330 
331 int
lws_dhcpc4_parse(lws_dhcpc_req_t * r,void * in,size_t len)332 lws_dhcpc4_parse(lws_dhcpc_req_t *r, void *in, size_t len)
333 {
334 	uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end;
335 	int n, m;
336 
337 	switch (r->state) {
338 	case LDHC_INIT:		/* expect DHCPOFFER */
339 	case LDHC_REQUESTING:	/* expect DHCPACK */
340 		/*
341 		 * We should check carefully if we like what we were
342 		 * sent... anything can spam us with crafted replies
343 		 */
344 		if (len < 0x100)
345 			break;
346 
347 		p = (uint8_t *)in + 28; /* skip to UDP payload */
348 		if (p[0] != 2 || p[1] != 1 || p[2] != 6)
349 			break;
350 
351 		if (memcmp(&p[4], r->xid, 4))	/* must be our xid */
352 			break;
353 
354 		if (memcmp(&p[0x1c], r->is.mac, 6)) /* our netif mac? */
355 			break;
356 
357 		/* the DHCP magic cookie must be in place */
358 		if (lws_ser_ru32be(&p[0xec]) != 0x63825363)
359 			break;
360 
361 		/* "your" client IP address */
362 		lws_sa46_set_ipv4(r, LWSDH_SA46_IP, p + 0x10);
363 		/* IP of next server used in bootstrap */
364 		lws_sa46_set_ipv4(r, LWSDH_SA46_DHCP_SERVER, p + 0x14);
365 
366 		/* it looks legit so far... look at the options */
367 
368 		end = (uint8_t *)in + len;
369 		p += 0xec + 4;
370 		while (p < end) {
371 			uint8_t c = *p++;
372 			uint8_t l = 0;
373 
374 			if (c && c != 0xff) {
375 				/* pad 0 and EOT 0xff have no length */
376 				l = *p++;
377 				if (!l) {
378 					lwsl_err("%s: zero length\n",
379 							__func__);
380 					goto broken;
381 				}
382 				if (p + l > end) {
383 					/* ...nice try... */
384 					lwsl_err("%s: bad len\n",
385 							__func__);
386 					goto broken;
387 				}
388 			}
389 
390 			if (c == 0xff) /* end of options */
391 				break;
392 
393 			m = 0;
394 			switch (c) {
395 			case LWSDHC4POPT_SUBNET_MASK:
396 				n = LWSDH_IPV4_SUBNET_MASK;
397 				goto get_ipv4;
398 
399 			case LWSDHC4POPT_ROUTER:
400 				lws_sa46_set_ipv4(r, LWSDH_SA46_IPV4_ROUTER, p);
401 				break;
402 
403 			case LWSDHC4POPT_TIME_SERVER:
404 				lws_sa46_set_ipv4(r, LWSDH_SA46_NTP_SERVER, p);
405 				break;
406 
407 			case LWSDHC4POPT_BROADCAST_ADS:
408 				n = LWSDH_IPV4_BROADCAST;
409 				goto get_ipv4;
410 
411 			case LWSDHC4POPT_LEASE_TIME:
412 				n = LWSDH_LEASE_SECS;
413 				goto get_ipv4;
414 
415 			case LWSDHC4POPT_RENEWAL_TIME: /* AKA T1 */
416 				n = LWSDH_RENEWAL_SECS;
417 				goto get_ipv4;
418 
419 			case LWSDHC4POPT_REBINDING_TIME: /* AKA T2 */
420 				n = LWSDH_REBINDING_SECS;
421 				goto get_ipv4;
422 
423 			case LWSDHC4POPT_DNSERVER:
424 				if (l & 3)
425 					break;
426 				m = LWSDH_SA46_DNS_SRV_1;
427 				while (l && m - LWSDH_SA46_DNS_SRV_1 < 4) {
428 					lws_sa46_set_ipv4(r, (unsigned int)m++, p);
429 					l = (uint8_t)(l - 4);
430 					p += 4;
431 				}
432 				break;
433 
434 			case LWSDHC4POPT_DOMAIN_NAME:
435 				m = l;
436 				if (m > (int)sizeof(r->is.domain) - 1)
437 					m = sizeof(r->is.domain) - 1;
438 				lws_strnncpy(r->is.domain, (const char *)p,
439 					 (unsigned int)m, sizeof(r->is.domain));
440 				break;
441 
442 			case LWSDHC4POPT_MESSAGE_TYPE:
443 				/*
444 				 * Confirm this is the right message
445 				 * for the state of the negotiation
446 				 */
447 				if (r->state == LDHC_INIT && *p != LWSDHC4POFFER)
448 					goto broken;
449 				if (r->state == LDHC_REQUESTING &&
450 				    *p != LWSDHC4PACK)
451 					goto broken;
452 				break;
453 
454 			default:
455 				break;
456 			}
457 
458 			p += l;
459 			continue;
460 get_ipv4:
461 			if (l >= 4)
462 				r->is.nums[n] = ntohl(lws_ser_ru32be(p));
463 			p += l;
464 			continue;
465 broken:
466 			memset(r->is.sa46, 0, sizeof(r->is.sa46));
467 			break;
468 		}
469 
470 #if defined(_DEBUG)
471 		/* dump what we have parsed out */
472 
473 		for (n = 0; n < (int)_LWSDH_NUMS_COUNT; n++) {
474 			m = (int)ntohl(r->is.nums[n]);
475 			lwsl_info("%s: %d: 0x%x\n", __func__, n, m);
476 		}
477 
478 		for (n = 0; n < (int)_LWSDH_SA46_COUNT; n++) {
479 			lws_sa46_write_numeric_address(&r->is.sa46[n],
480 						       (char *)pkt, 48);
481 			lwsl_info("%s: %d: %s\n", __func__, n, pkt);
482 		}
483 #endif
484 
485 		/*
486 		 * Having seen everything in there... do we really feel
487 		 * we could use it?  Everything critical is there?
488 		 */
489 
490 		if (!r->is.sa46[LWSDH_SA46_IP].sa4.sin_family ||
491 		    !r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_family ||
492 		    !r->is.sa46[LWSDH_SA46_IPV4_ROUTER].sa4.sin_family ||
493 		    !r->is.nums[LWSDH_IPV4_SUBNET_MASK] ||
494 		    !r->is.nums[LWSDH_LEASE_SECS] ||
495 		    !r->is.sa46[LWSDH_SA46_DNS_SRV_1].sa4.sin_family) {
496 			lwsl_notice("%s: rejecting on incomplete\n", __func__);
497 			memset(r->is.sa46, 0, sizeof(r->is.sa46));
498 			break;
499 		}
500 
501 		/*
502 		 * Network layout has to be internally consistent...
503 		 * DHCP server has to be reachable by broadcast and
504 		 * default route has to be on same subnet
505 		 */
506 
507 		if ((r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr &
508 					r->is.nums[LWSDH_IPV4_SUBNET_MASK]) !=
509 		    (r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_addr.s_addr &
510 				        r->is.nums[LWSDH_IPV4_SUBNET_MASK])) {
511 			lwsl_notice("%s: rejecting on srv %x reachable on mask %x\n",
512 					__func__, r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr,
513 					r->is.nums[LWSDH_IPV4_SUBNET_MASK]);
514 			break;
515 		}
516 
517 		if (r->state == LDHC_INIT) {
518 			lwsl_info("%s: moving to REQ\n", __func__);
519 			r->state = LDHC_REQUESTING;
520 			lws_callback_on_writable(r->wsi_raw);
521 			//break;
522 		}
523 
524 		return 0;
525 
526 	default:
527 		break;
528 	}
529 
530 	return 1;
531 }
532 
533