diff --git a/.gitignore b/.gitignore index fde9c9af4..47f7a9078 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ doc /b1/ /destdir/ /bb1/ +/bb3/ diff --git a/include/libwebsockets/lws-system.h b/include/libwebsockets/lws-system.h index 7b2240515..1feef7967 100644 --- a/include/libwebsockets/lws-system.h +++ b/include/libwebsockets/lws-system.h @@ -269,7 +269,36 @@ __lws_system_attach(struct lws_context *context, int tsi, lws_attach_cb_t cb, struct lws_attach_item **get); -typedef int (*dhcpc_cb_t)(void *opaque, int af, uint8_t *ip, int ip_len); +enum { + LWSDH_IPV4_SUBNET_MASK = 0, + LWSDH_IPV4_BROADCAST, + LWSDH_LEASE_SECS, + LWSDH_REBINDING_SECS, + LWSDH_RENEWAL_SECS, + + _LWSDH_NUMS_COUNT, + + LWSDH_SA46_IP = 0, + LWSDH_SA46_DNS_SRV_1, + LWSDH_SA46_DNS_SRV_2, + LWSDH_SA46_DNS_SRV_3, + LWSDH_SA46_DNS_SRV_4, + LWSDH_SA46_IPV4_ROUTER, + LWSDH_SA46_NTP_SERVER, + LWSDH_SA46_DHCP_SERVER, + + _LWSDH_SA46_COUNT, +}; + +typedef struct lws_dhcpc_ifstate { + char ifname[16]; + char domain[64]; + uint8_t mac[6]; + uint32_t nums[_LWSDH_NUMS_COUNT]; + lws_sockaddr46 sa46[_LWSDH_SA46_COUNT]; +} lws_dhcpc_ifstate_t; + +typedef int (*dhcpc_cb_t)(void *opaque, lws_dhcpc_ifstate_t *is); /** * lws_dhcpc_request() - add a network interface to dhcpc management diff --git a/lib/core/context.c b/lib/core/context.c index f3939e23f..3517b91c2 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -278,7 +278,7 @@ lws_system_smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, lws_ntpc_trigger(cx); #endif -#if defined(LWS_WITH_SYS_DHCP_CLIENT) +#if defined(LWS_WITH_SYS_DHCP_CLIENT) && 0 /* * Any network interface linkup triggers DHCP */ @@ -1148,7 +1148,7 @@ lws_create_context(const struct lws_context_creation_info *info) extern const struct lws_protocols lws_system_protocol_ntpc; #endif #if defined(LWS_WITH_SYS_DHCP_CLIENT) - extern const struct lws_protocols lws_system_protocol_dhcpc; + extern const struct lws_protocols lws_system_protocol_dhcpc4; #endif n = 0; @@ -1159,7 +1159,7 @@ lws_create_context(const struct lws_context_creation_info *info) pp[n++] = &lws_system_protocol_ntpc; #endif #if defined(LWS_WITH_SYS_DHCP_CLIENT) - pp[n++] = &lws_system_protocol_dhcpc; + pp[n++] = &lws_system_protocol_dhcpc4; #endif pp[n] = NULL; @@ -1766,20 +1766,6 @@ next: * is destroyed. We can remove the logical vhosts. */ -#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_SECURE_STREAMS) && \ - !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) - - while (context->server_der_list) { - struct lws_ss_x509 *x = context->server_der_list; - - context->server_der_list = x->next; - lws_free((void *)x->ca_der); - } - - if (context->ac_policy) - lwsac_free(&context->ac_policy); -#endif - #if defined(LWS_WITH_SYS_STATE) && defined(LWS_WITH_NETWORK) lws_state_transition(&context->mgr_system, LWS_SYSTATE_POLICY_INVALID); #endif @@ -1945,6 +1931,20 @@ next: lws_system_blob_destroy( lws_system_get_blob(context, (lws_system_blob_item_t)n, 0)); +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_SECURE_STREAMS) && \ + !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + + while (context->server_der_list) { + struct lws_ss_x509 *x = context->server_der_list; + + context->server_der_list = x->next; + lws_free((void *)x->ca_der); + } + + if (context->ac_policy) + lwsac_free(&context->ac_policy); +#endif + /* * Context lock is about to go away */ diff --git a/lib/plat/freertos/freertos-sockets.c b/lib/plat/freertos/freertos-sockets.c index 82ac5518a..33e731989 100644 --- a/lib/plat/freertos/freertos-sockets.c +++ b/lib/plat/freertos/freertos-sockets.c @@ -317,8 +317,7 @@ lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) } int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) +lws_plat_ifconfig(int fd, lws_dhcpc_ifstate_t *is) { lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); diff --git a/lib/plat/unix/private-lib-plat-unix.h b/lib/plat/unix/private-lib-plat-unix.h index b6e4ee8aa..1aa9e8079 100644 --- a/lib/plat/unix/private-lib-plat-unix.h +++ b/lib/plat/unix/private-lib-plat-unix.h @@ -161,9 +161,9 @@ wsi_from_fd(const struct lws_context *context, int fd); int insert_wsi(const struct lws_context *context, struct lws *wsi); +struct lws_dhcpc_ifstate; int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip); +lws_plat_ifconfig(int fd, struct lws_dhcpc_ifstate *is); void delete_from_fd(const struct lws_context *context, int fd); diff --git a/lib/plat/unix/unix-sockets.c b/lib/plat/unix/unix-sockets.c index f6f9ce9b5..8b70570f0 100644 --- a/lib/plat/unix/unix-sockets.c +++ b/lib/plat/unix/unix-sockets.c @@ -470,56 +470,60 @@ lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) } int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) +lws_plat_ifconfig(int fd, lws_dhcpc_ifstate_t *is) { #if defined(__linux__) - struct sockaddr_in sin; struct rtentry route; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); memset(&route, 0, sizeof(route)); - memset(&sin, 0, sizeof(sin)); - lws_strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + lws_strncpy(ifr.ifr_name, is->ifname, IFNAMSIZ); - lws_plat_if_up(ifname, fd, 0); + lws_plat_if_up(is->ifname, fd, 0); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(*(uint32_t *)ip); - - memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + memcpy(&ifr.ifr_addr, &is->sa46[LWSDH_SA46_IP], sizeof(struct sockaddr)); if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { lwsl_err("%s: SIOCSIFADDR fail\n", __func__); return 1; } - sin.sin_addr.s_addr = htonl(*(uint32_t *)mask_ip); - memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); - if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { - lwsl_err("%s: SIOCSIFNETMASK fail\n", __func__); - return 1; - } + if (is->sa46[LWSDH_SA46_IP].sa4.sin_family == AF_INET) { + struct sockaddr_in sin; - lws_plat_if_up(ifname, fd, 1); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = *(uint32_t *)&is->nums[LWSDH_IPV4_SUBNET_MASK]; + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { + lwsl_err("%s: SIOCSIFNETMASK fail\n", __func__); + return 1; + } - sin.sin_addr.s_addr = htonl(*(uint32_t *)gateway_ip); - memcpy(&route.rt_gateway, &sin, sizeof(struct sockaddr)); + lws_plat_if_up(is->ifname, fd, 1); - sin.sin_addr.s_addr = 0; - memcpy(&route.rt_dst, &sin, sizeof(struct sockaddr)); - memcpy(&route.rt_genmask, &sin, sizeof(struct sockaddr)); + memcpy(&route.rt_gateway, + &is->sa46[LWSDH_SA46_IPV4_ROUTER].sa4, + sizeof(struct sockaddr)); - route.rt_flags = RTF_UP | RTF_GATEWAY; - route.rt_metric = 100; - route.rt_dev = (char *)ifname; + sin.sin_addr.s_addr = 0; + memcpy(&route.rt_dst, &sin, sizeof(struct sockaddr)); + memcpy(&route.rt_genmask, &sin, sizeof(struct sockaddr)); - if (ioctl(fd, SIOCADDRT, &route) < 0) { - lwsl_err("%s: SIOCADDRT 0x%x fail: %d\n", __func__, - (unsigned int)htonl(*(uint32_t *)gateway_ip), LWS_ERRNO); - return 1; - } + route.rt_flags = RTF_UP | RTF_GATEWAY; + route.rt_metric = 100; + route.rt_dev = (char *)is->ifname; + + if (ioctl(fd, SIOCADDRT, &route) < 0) { + lwsl_err("%s: SIOCADDRT 0x%x fail: %d\n", __func__, + (unsigned int)htonl(*(uint32_t *)&is-> + sa46[LWSDH_SA46_IPV4_ROUTER]. + sa4.sin_addr.s_addr), LWS_ERRNO); + return 1; + } + } else + lws_plat_if_up(is->ifname, fd, 1); return 0; #else diff --git a/lib/plat/windows/windows-sockets.c b/lib/plat/windows/windows-sockets.c index d526175d1..2db5441c7 100644 --- a/lib/plat/windows/windows-sockets.c +++ b/lib/plat/windows/windows-sockets.c @@ -383,8 +383,7 @@ lws_plat_BINDTODEVICE(lws_sockfd_type fd, const char *ifname) } int -lws_plat_ifconfig_ip(const char *ifname, int fd, uint8_t *ip, uint8_t *mask_ip, - uint8_t *gateway_ip) +lws_plat_ifconfig(int fd, uint8_t *ip, lws_dhcpc_ifstate_t *is) { lwsl_err("%s: UNIMPLEMENTED on this platform\n", __func__); diff --git a/lib/system/CMakeLists.txt b/lib/system/CMakeLists.txt index 7b2c42bb5..78cc24006 100644 --- a/lib/system/CMakeLists.txt +++ b/lib/system/CMakeLists.txt @@ -49,7 +49,8 @@ if (LWS_WITH_NETWORK) if (LWS_WITH_SYS_DHCP_CLIENT) list(APPEND SOURCES - system/dhcpclient/dhcpclient.c) + system/dhcpclient/dhcpclient.c + system/dhcpclient/dhcpc4.c) endif() if (LWS_WITH_SYS_SMD) diff --git a/lib/system/dhcpclient/dhcpc4.c b/lib/system/dhcpclient/dhcpc4.c new file mode 100644 index 000000000..c841d2bc9 --- /dev/null +++ b/lib/system/dhcpclient/dhcpc4.c @@ -0,0 +1,532 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * The protocol part of dhcp4 client + */ + +#include "private-lib-core.h" +#include "private-lib-system-dhcpclient.h" + +#define LDHC_OP_BOOTREQUEST 1 +#define LDHC_OP_BOOTREPLY 2 + +/* + * IPv4... max total 576 + * + * 0 1 2 3 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | op (1) | htype (1) | hlen (1) | hops (1) | + * +---------------+---------------+---------------+---------------+ + * | +04 xid (4) | + * +-------------------------------+-------------------------------+ + * | +08 secs (2) | +0a flags (2) | + * +-------------------------------+-------------------------------+ + * | +0C ciaddr (4) client IP | + * +---------------------------------------------------------------+ + * | +10 yiaddr (4) your IP | + * +---------------------------------------------------------------+ + * | +14 siaddr (4) server IP | + * +---------------------------------------------------------------+ + * | +18 giaddr (4) gateway IP | + * +---------------------------------------------------------------+ + * | | + * | +1C chaddr (16) client HWADDR | + * +---------------------------------------------------------------+ + * | | + * | +2C sname (64) | + * +---------------------------------------------------------------+ + * | | + * | +6C file (128) | + * +---------------------------------------------------------------+ + * | | + * | +EC options (variable) | + * +---------------------------------------------------------------+ + */ + +static const uint8_t rawdisc4[] = { + 0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP, + 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, + 0, 68, 0, 67, 0, 0, 0, 0 +}; + +static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ }; +static const lws_retry_bo_t bo2 = { + botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 }; + +static int +lws_dhcpc4_prep(uint8_t *start, unsigned int bufsiz, lws_dhcpc_req_t *r, int op) +{ + uint8_t *p = start; + + memset(start, 0, bufsiz); + + *p++ = 1; + *p++ = 1; + *p++ = 6; /* sizeof ethernet MAC */ + + memcpy(p + 1, r->xid, 4); + +// p[7] = 0x80; /* broadcast flag */ + + p += 0x1c - 3; + + if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd, + (const char *)&r[1], r->is.mac, 6) < 0) + return -1; + + memcpy(p, r->is.mac, 6); + + p += 16 + 64 + 128; + + *p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */ + *p++ = 0x82; + *p++ = 0x53; + *p++ = 0x63; + + *p++ = LWSDHC4POPT_MESSAGE_TYPE; + *p++ = 1; /* length */ + *p++ = (uint8_t)op; + + switch (op) { + case LWSDHC4PDISCOVER: + *p++ = LWSDHC4POPT_PARAM_REQ_LIST; + *p++ = 4; /* length */ + *p++ = LWSDHC4POPT_SUBNET_MASK; + *p++ = LWSDHC4POPT_ROUTER; + *p++ = LWSDHC4POPT_DNSERVER; + *p++ = LWSDHC4POPT_DOMAIN_NAME; + break; + + case LWSDHC4PREQUEST: + if (r->is.sa46[LWSDH_SA46_IP].sa4.sin_family != AF_INET) + break; + *p++ = LWSDHC4POPT_REQUESTED_ADS; + *p++ = 4; /* length */ + lws_ser_wu32be(p, r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr); + p += 4; + *p++ = LWSDHC4POPT_SERVER_ID; + *p++ = 4; /* length */ + lws_ser_wu32be(p, r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_addr.s_addr); + p += 4; + break; + } + + *p++ = LWSDHC4POPT_END_OPTIONS; + + return lws_ptr_diff(p, start); +} + +static int +callback_dhcpc4(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user; + uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE; + int n, m; + + switch (reason) { + + case LWS_CALLBACK_RAW_ADOPT: + lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__); + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("%s: udp conn failed\n", __func__); + + /* fallthru */ + case LWS_CALLBACK_RAW_CLOSE: + lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__); + if (!r) + break; + r->wsi_raw = NULL; + lws_sul_cancel(&r->sul_write); + if (r->state != LDHC_BOUND) { + r->state = LDHC_INIT; + lws_retry_sul_schedule(r->context, 0, &r->sul_conn, + &bo2, lws_dhcpc4_retry_conn, + &r->retry_count_conn); + } + break; + + case LWS_CALLBACK_RAW_RX: + + if (lws_dhcpc4_parse(r, in, len)) + break; + + /* + * that's it... commit to the configuration + */ + + /* set up our network interface as offered */ + + if (lws_plat_ifconfig(r->wsi_raw->desc.sockfd, &r->is)) + /* + * Problem setting the IP... maybe something + * transient like racing with NetworkManager? + * Since the sul retries are still around it + * will retry + */ + return -1; + + /* clear timeouts related to the broadcast socket */ + + lws_sul_cancel(&r->sul_write); + lws_sul_cancel(&r->sul_conn); + + lwsl_notice("%s: DHCP configured %s\n", __func__, + (const char *)&r[1]); + r->state = LDHC_BOUND; + + lws_state_transition_steps(&wsi->a.context->mgr_system, + LWS_SYSTATE_OPERATIONAL); + + r->cb(r->opaque, &r->is); + + r->wsi_raw = NULL; + + return -1; /* close the broadcast wsi */ + + case LWS_CALLBACK_RAW_WRITEABLE: + + if (!r) + break; + + /* + * UDP is not reliable, it can be locally dropped, or dropped + * by any intermediary or the remote peer. So even though we + * will do the write in a moment, we schedule another request + * for rewrite according to the wsi retry policy. + * + * If the result came before, we'll cancel it in the close flow. + * + * If we have already reached the end of our concealed retries + * in the policy, just close without another write. + */ + if (lws_dll2_is_detached(&r->sul_write.list) && + lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write, + lws_dhcpc_retry_write, + &r->retry_count_write)) { + /* we have reached the end of our concealed retries */ + lwsl_warn("%s: concealed retries done, failing\n", + __func__); + goto retry_conn; + } + + switch (r->state) { + case LDHC_INIT: + n = LWSDHC4PDISCOVER; + goto bcast; + + case LDHC_REQUESTING: + n = LWSDHC4PREQUEST; + + /* fallthru */ +bcast: + n = lws_dhcpc4_prep(p + 28, (unsigned int) + (sizeof(pkt) - LWS_PRE - 28), r, n); + if (n < 0) { + lwsl_err("%s: failed to prep\n", __func__); + break; + } + + m = lws_plat_rawudp_broadcast(p, rawdisc4, + LWS_ARRAY_SIZE(rawdisc4), + (size_t)(n + 28), + r->wsi_raw->desc.sockfd, + (const char *)&r[1]); + if (m < 0) + lwsl_err("%s: Failed to write dhcp client req: " + "%d %d, errno %d\n", __func__, + n, m, LWS_ERRNO); + break; + default: + break; + } + + return 0; + +retry_conn: + lws_retry_sul_schedule(wsi->a.context, 0, &r->sul_conn, &bo2, + lws_dhcpc4_retry_conn, + &r->retry_count_conn); + + return -1; + + default: + break; + } + + return 0; +} + +struct lws_protocols lws_system_protocol_dhcpc4 = + { "lws-dhcp4client", callback_dhcpc4, 0, 128, }; + +void +lws_dhcpc4_retry_conn(struct lws_sorted_usec_list *sul) +{ + lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn); + + if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list)) + return; + + /* create the UDP socket aimed at the server */ + + r->retry_count_write = 0; + r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0", + 68, LWS_CAUDP_PF_PACKET | + LWS_CAUDP_BROADCAST, + "lws-dhcp4client", (const char *)&r[1], + NULL, NULL, &bo2); + lwsl_debug("%s: created wsi_raw: %s\n", __func__, lws_wsi_tag(r->wsi_raw)); + if (!r->wsi_raw) { + lwsl_err("%s: unable to create udp skt\n", __func__); + + lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2, + lws_dhcpc4_retry_conn, + &r->retry_count_conn); + + return; + } + + /* force the network if up */ + lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0); + lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1); + + r->wsi_raw->user_space = r; + r->wsi_raw->user_space_externally_allocated = 1; + + lws_get_random(r->wsi_raw->a.context, r->xid, 4); +} + +static void +lws_sa46_set_ipv4(lws_dhcpc_req_t *r, unsigned int which, uint8_t *p) +{ + r->is.sa46[which].sa4.sin_family = AF_INET; + r->is.sa46[which].sa4.sin_addr.s_addr = ntohl(lws_ser_ru32be(p)); +} + +int +lws_dhcpc4_parse(lws_dhcpc_req_t *r, void *in, size_t len) +{ + uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end; + int n, m; + + switch (r->state) { + case LDHC_INIT: /* expect DHCPOFFER */ + case LDHC_REQUESTING: /* expect DHCPACK */ + /* + * We should check carefully if we like what we were + * sent... anything can spam us with crafted replies + */ + if (len < 0x100) + break; + + p = (uint8_t *)in + 28; /* skip to UDP payload */ + if (p[0] != 2 || p[1] != 1 || p[2] != 6) + break; + + if (memcmp(&p[4], r->xid, 4)) /* must be our xid */ + break; + + if (memcmp(&p[0x1c], r->is.mac, 6)) /* our netif mac? */ + break; + + /* the DHCP magic cookie must be in place */ + if (lws_ser_ru32be(&p[0xec]) != 0x63825363) + break; + + /* "your" client IP address */ + lws_sa46_set_ipv4(r, LWSDH_SA46_IP, p + 0x10); + /* IP of next server used in bootstrap */ + lws_sa46_set_ipv4(r, LWSDH_SA46_DHCP_SERVER, p + 0x14); + + /* it looks legit so far... look at the options */ + + end = (uint8_t *)in + len; + p += 0xec + 4; + while (p < end) { + uint8_t c = *p++; + uint8_t l = 0; + + if (c && c != 0xff) { + /* pad 0 and EOT 0xff have no length */ + l = *p++; + if (!l) { + lwsl_err("%s: zero length\n", + __func__); + goto broken; + } + if (p + l > end) { + /* ...nice try... */ + lwsl_err("%s: bad len\n", + __func__); + goto broken; + } + } + + if (c == 0xff) /* end of options */ + break; + + m = 0; + switch (c) { + case LWSDHC4POPT_SUBNET_MASK: + n = LWSDH_IPV4_SUBNET_MASK; + goto get_ipv4; + + case LWSDHC4POPT_ROUTER: + lws_sa46_set_ipv4(r, LWSDH_SA46_IPV4_ROUTER, p); + break; + + case LWSDHC4POPT_TIME_SERVER: + lws_sa46_set_ipv4(r, LWSDH_SA46_NTP_SERVER, p); + break; + + case LWSDHC4POPT_BROADCAST_ADS: + n = LWSDH_IPV4_BROADCAST; + goto get_ipv4; + + case LWSDHC4POPT_LEASE_TIME: + n = LWSDH_LEASE_SECS; + goto get_ipv4; + + case LWSDHC4POPT_RENEWAL_TIME: /* AKA T1 */ + n = LWSDH_RENEWAL_SECS; + goto get_ipv4; + + case LWSDHC4POPT_REBINDING_TIME: /* AKA T2 */ + n = LWSDH_REBINDING_SECS; + goto get_ipv4; + + case LWSDHC4POPT_DNSERVER: + if (l & 3) + break; + m = LWSDH_SA46_DNS_SRV_1; + while (l && m - LWSDH_SA46_DNS_SRV_1 < 4) { + lws_sa46_set_ipv4(r, (unsigned int)m++, p); + l = (uint8_t)(l - 4); + p += 4; + } + break; + + case LWSDHC4POPT_DOMAIN_NAME: + m = l; + if (m > (int)sizeof(r->is.domain) - 1) + m = sizeof(r->is.domain) - 1; + lws_strnncpy(r->is.domain, (const char *)p, + (unsigned int)m, sizeof(r->is.domain)); + break; + + case LWSDHC4POPT_MESSAGE_TYPE: + /* + * Confirm this is the right message + * for the state of the negotiation + */ + if (r->state == LDHC_INIT && *p != LWSDHC4POFFER) + goto broken; + if (r->state == LDHC_REQUESTING && + *p != LWSDHC4PACK) + goto broken; + break; + + default: + break; + } + + p += l; + continue; +get_ipv4: + if (l >= 4) + r->is.nums[n] = ntohl(lws_ser_ru32be(p)); + p += l; + continue; +broken: + memset(r->is.sa46, 0, sizeof(r->is.sa46)); + break; + } + +#if defined(_DEBUG) + /* dump what we have parsed out */ + + for (n = 0; n < (int)_LWSDH_NUMS_COUNT; n++) { + m = (int)ntohl(r->is.nums[n]); + lwsl_info("%s: %d: 0x%x\n", __func__, n, m); + } + + for (n = 0; n < (int)_LWSDH_SA46_COUNT; n++) { + lws_sa46_write_numeric_address(&r->is.sa46[n], + (char *)pkt, 48); + lwsl_info("%s: %d: %s\n", __func__, n, pkt); + } +#endif + + /* + * Having seen everything in there... do we really feel + * we could use it? Everything critical is there? + */ + + if (!r->is.sa46[LWSDH_SA46_IP].sa4.sin_family || + !r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_family || + !r->is.sa46[LWSDH_SA46_IPV4_ROUTER].sa4.sin_family || + !r->is.nums[LWSDH_IPV4_SUBNET_MASK] || + !r->is.nums[LWSDH_LEASE_SECS] || + !r->is.sa46[LWSDH_SA46_DNS_SRV_1].sa4.sin_family) { + lwsl_notice("%s: rejecting on incomplete\n", __func__); + memset(r->is.sa46, 0, sizeof(r->is.sa46)); + break; + } + + /* + * Network layout has to be internally consistent... + * DHCP server has to be reachable by broadcast and + * default route has to be on same subnet + */ + + if ((r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr & + r->is.nums[LWSDH_IPV4_SUBNET_MASK]) != + (r->is.sa46[LWSDH_SA46_DHCP_SERVER].sa4.sin_addr.s_addr & + r->is.nums[LWSDH_IPV4_SUBNET_MASK])) { + lwsl_notice("%s: rejecting on srv %x reachable on mask %x\n", + __func__, r->is.sa46[LWSDH_SA46_IP].sa4.sin_addr.s_addr, + r->is.nums[LWSDH_IPV4_SUBNET_MASK]); + break; + } + + if (r->state == LDHC_INIT) { + lwsl_info("%s: moving to REQ\n", __func__); + r->state = LDHC_REQUESTING; + lws_callback_on_writable(r->wsi_raw); + //break; + } + + return 0; + + default: + break; + } + + return 1; +} + diff --git a/lib/system/dhcpclient/dhcpclient.c b/lib/system/dhcpclient/dhcpclient.c index 438757ae5..ca628e39a 100644 --- a/lib/system/dhcpclient/dhcpclient.c +++ b/lib/system/dhcpclient/dhcpclient.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2020 Andy Green + * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -25,205 +25,7 @@ #include "private-lib-core.h" #include "private-lib-system-dhcpclient.h" -typedef enum { - LDHC_INIT_REBOOT, - LDHC_REBOOTING, /* jitterwait */ - LDHC_INIT, /* issue DHCPDISCOVER */ - LDHC_SELECTING, - LDHC_REQUESTING, - LDHC_REBINDING, - LDHC_BOUND, - LDHC_RENEWING -} lws_dhcpc_state_t; - -enum { - LWSDHCPDISCOVER = 1, - LWSDHCPOFFER, - LWSDHCPREQUEST, - LWSDHCPDECLINE, - LWSDHCPACK, - LWSDHCPNACK, - LWSDHCPRELEASE, - - IPV4_PROPOSED = 0, - IPV4_SERVER, - IPV4_ROUTER, - IPV4_SUBNET_MASK, - IPV4_BROADCAST, - IPV4_TIME_SERVER, - IPV4_DNS_SRV_1, - IPV4_DNS_SRV_2, - IPV4_DNS_SRV_3, - IPV4_DNS_SRV_4, - IPV4_LEASE_SECS, - IPV4_REBINDING_SECS, - IPV4_RENEWAL_SECS, - - _IPV4_COUNT, - - LWSDHCPOPT_PAD = 0, - LWSDHCPOPT_SUBNET_MASK = 1, - LWSDHCPOPT_TIME_OFFSET = 2, - LWSDHCPOPT_ROUTER = 3, - LWSDHCPOPT_TIME_SERVER = 4, - LWSDHCPOPT_NAME_SERVER = 5, - LWSDHCPOPT_DNSERVER = 6, - LWSDHCPOPT_LOG_SERVER = 7, - LWSDHCPOPT_COOKIE_SERVER = 8, - LWSDHCPOPT_LPR_SERVER = 9, - LWSDHCPOPT_IMPRESS_SERVER = 10, - LWSDHCPOPT_RESLOC_SERVER = 11, - LWSDHCPOPT_HOST_NAME = 12, - LWSDHCPOPT_BOOTFILE_SIZE = 13, - LWSDHCPOPT_MERIT_DUMP_FILE = 14, - LWSDHCPOPT_DOMAIN_NAME = 15, - LWSDHCPOPT_SWAP_SERVER = 16, - LWSDHCPOPT_ROOT_PATH = 17, - LWSDHCPOPT_EXTENSIONS_PATH = 18, - LWSDHCPOPT_BROADCAST_ADS = 28, - - LWSDHCPOPT_REQUESTED_ADS = 50, - LWSDHCPOPT_LEASE_TIME = 51, - LWSDHCPOPT_OPTION_OVERLOAD = 52, - LWSDHCPOPT_MESSAGE_TYPE = 53, - LWSDHCPOPT_SERVER_ID = 54, - LWSDHCPOPT_PARAM_REQ_LIST = 55, - LWSDHCPOPT_MESSAGE = 56, - LWSDHCPOPT_MAX_DHCP_MSG_SIZE = 57, - LWSDHCPOPT_RENEWAL_TIME = 58, /* AKA T1 */ - LWSDHCPOPT_REBINDING_TIME = 59, /* AKA T2 */ - LWSDHCPOPT_VENDOR_CLASS_ID = 60, - LWSDHCPOPT_CLIENT_ID = 61, - - LWSDHCPOPT_END_OPTIONS = 255 -}; - -typedef struct lws_dhcpc_req { - lws_dll2_t list; - char domain[64]; - struct lws_context *context; - lws_sorted_usec_list_t sul_conn; - lws_sorted_usec_list_t sul_write; - dhcpc_cb_t cb; /* cb on completion / failure */ - void *opaque; /* ignored by lws, give to cb */ - - /* these are separated so we can close the bcast one asynchronously */ - struct lws *wsi_raw; /* for broadcast */ - lws_dhcpc_state_t state; - - uint32_t ipv4[_IPV4_COUNT]; - - uint16_t retry_count_conn; - uint16_t retry_count_write; - uint8_t mac[6]; - uint8_t xid[4]; - uint8_t af; /* address family */ -} lws_dhcpc_req_t; -/* interface name is overallocated here */ - -static const uint32_t botable2[] = { 1500, 1750, 5000 /* in case dog slow */ }; -static const lws_retry_bo_t bo2 = { - botable2, LWS_ARRAY_SIZE(botable2), LWS_RETRY_CONCEAL_ALWAYS, 0, 0, 20 }; - -static const uint8_t rawdisc[] = { - 0x45, 0x00, 0, 0, 0, 0, 0x40, 0, 0x2e, IPPROTO_UDP, - 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, - 0, 68, 0, 67, 0, 0, 0, 0 -}; - -#define LDHC_OP_BOOTREQUEST 1 -#define LDHC_OP_BOOTREPLY 2 - -/* - * IPv4... max total 576 - * - * 0 1 2 3 - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | op (1) | htype (1) | hlen (1) | hops (1) | - * +---------------+---------------+---------------+---------------+ - * | +04 xid (4) | - * +-------------------------------+-------------------------------+ - * | +08 secs (2) | +0a flags (2) | - * +-------------------------------+-------------------------------+ - * | +0C ciaddr (4) client IP | - * +---------------------------------------------------------------+ - * | +10 yiaddr (4) your IP | - * +---------------------------------------------------------------+ - * | +14 siaddr (4) server IP | - * +---------------------------------------------------------------+ - * | +18 giaddr (4) gateway IP | - * +---------------------------------------------------------------+ - * | | - * | +1C chaddr (16) client HWADDR | - * +---------------------------------------------------------------+ - * | | - * | +2C sname (64) | - * +---------------------------------------------------------------+ - * | | - * | +6C file (128) | - * +---------------------------------------------------------------+ - * | | - * | +EC options (variable) | - * +---------------------------------------------------------------+ - */ - -#if defined(_DEBUG) -static const char *dhcp_entry_names[] = { - "proposed ip", - "dhcp server", - "router", - "subnet mask", - "broadcast", - "time server", - "dns1", - "dns2", - "dns3", - "dns4", - "lease secs", - "rebinding secs", - "renewal secs", -}; -#endif - -static void -lws_dhcpc_retry_conn(struct lws_sorted_usec_list *sul) -{ - lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_conn); - - if (r->wsi_raw || !lws_dll2_is_detached(&r->sul_conn.list)) - return; - - /* create the UDP socket aimed at the server */ - - r->retry_count_write = 0; - r->wsi_raw = lws_create_adopt_udp(r->context->vhost_system, "0.0.0.0", - 68, LWS_CAUDP_PF_PACKET | - LWS_CAUDP_BROADCAST, - "lws-dhcpclient", (const char *)&r[1], - NULL, NULL, &bo2); - lwsl_debug("%s: created wsi_raw: %s\n", __func__, lws_wsi_tag(r->wsi_raw)); - if (!r->wsi_raw) { - lwsl_err("%s: unable to create udp skt\n", __func__); - - lws_retry_sul_schedule(r->context, 0, &r->sul_conn, &bo2, - lws_dhcpc_retry_conn, - &r->retry_count_conn); - - return; - } - - /* force the network if up */ - lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 0); - lws_plat_if_up((const char *)&r[1], r->wsi_raw->desc.sockfd, 1); - - r->wsi_raw->user_space = r; - r->wsi_raw->user_space_externally_allocated = 1; - - lws_get_random(r->wsi_raw->a.context, r->xid, 4); -} - -static void +void lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul) { lws_dhcpc_req_t *r = lws_container_of(sul, lws_dhcpc_req_t, sul_write); @@ -234,406 +36,6 @@ lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul) lws_callback_on_writable(r->wsi_raw); } -static int -lws_dhcpc_prep(uint8_t *start, unsigned int bufsiz, lws_dhcpc_req_t *r, int op) -{ - uint8_t *p = start; - - memset(start, 0, bufsiz); - - *p++ = 1; - *p++ = 1; - *p++ = 6; /* sizeof ethernet MAC */ - - memcpy(p + 1, r->xid, 4); - -// p[7] = 0x80; /* broadcast flag */ - - p += 0x1c - 3; - - if (lws_plat_ifname_to_hwaddr(r->wsi_raw->desc.sockfd, - (const char *)&r[1], r->mac, 6) < 0) - return -1; - - memcpy(p, r->mac, 6); - - p += 16 + 64 + 128; - - *p++ = 0x63; /* RFC2132 Magic Cookie indicates start of options */ - *p++ = 0x82; - *p++ = 0x53; - *p++ = 0x63; - - *p++ = LWSDHCPOPT_MESSAGE_TYPE; - *p++ = 1; /* length */ - *p++ = (uint8_t)op; - - switch (op) { - case LWSDHCPDISCOVER: - *p++ = LWSDHCPOPT_PARAM_REQ_LIST; - *p++ = 4; /* length */ - *p++ = 1; /* subnet mask */ - *p++ = 3; /* router */ - *p++ = 15; /* domain name */ - *p++ = 6; /* DNServer */ - break; - case LWSDHCPREQUEST: - *p++ = LWSDHCPOPT_REQUESTED_ADS; - *p++ = 4; /* length */ - lws_ser_wu32be(p, r->ipv4[IPV4_PROPOSED]); - p += 4; - *p++ = LWSDHCPOPT_SERVER_ID; - *p++ = 4; /* length */ - lws_ser_wu32be(p, r->ipv4[IPV4_SERVER]); - p += 4; - break; - } - - *p++ = LWSDHCPOPT_END_OPTIONS; - - return lws_ptr_diff(p, start); -} - -static int -callback_dhcpc(struct lws *wsi, enum lws_callback_reasons reason, void *user, - void *in, size_t len) -{ - lws_dhcpc_req_t *r = (lws_dhcpc_req_t *)user; - uint8_t pkt[LWS_PRE + 576], *p = pkt + LWS_PRE, *end; - int n, m; - - switch (reason) { - - case LWS_CALLBACK_RAW_ADOPT: - lwsl_debug("%s: LWS_CALLBACK_RAW_ADOPT\n", __func__); - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_err("%s: udp conn failed\n", __func__); - - /* fallthru */ - case LWS_CALLBACK_RAW_CLOSE: - lwsl_debug("%s: LWS_CALLBACK_RAW_CLOSE\n", __func__); - if (!r) - break; - r->wsi_raw = NULL; - lws_sul_cancel(&r->sul_write); - if (r->state != LDHC_BOUND) { - r->state = LDHC_INIT; - lws_retry_sul_schedule(r->context, 0, &r->sul_conn, - &bo2, lws_dhcpc_retry_conn, - &r->retry_count_conn); - } - break; - - case LWS_CALLBACK_RAW_RX: - - switch (r->state) { - case LDHC_INIT: /* expect DHCPOFFER */ - case LDHC_REQUESTING: /* expect DHCPACK */ - /* - * We should check carefully if we like what we were - * sent... anything can spam us with crafted replies - */ - if (len < 0x100) - break; - - p = (uint8_t *)in + 28; /* skip to UDP payload */ - if (p[0] != 2 || p[1] != 1 || p[2] != 6) - break; - - if (memcmp(&p[4], r->xid, 4)) /* must be our xid */ - break; - - if (memcmp(&p[0x1c], r->mac, 6)) /* our netif mac? */ - break; - - /* the DHCP magic cookie must be in place */ - if (lws_ser_ru32be(&p[0xec]) != 0x63825363) - break; - - r->ipv4[IPV4_PROPOSED] = lws_ser_ru32be(&p[0x10]); - r->ipv4[IPV4_SERVER] = lws_ser_ru32be(&p[0x14]); - - /* it looks legit so far... look at the options */ - - end = (uint8_t *)in + len; - p += 0xec + 4; - while (p < end) { - uint8_t c = *p++; - uint8_t l = 0; - - if (c && c != 0xff) { - /* pad 0 and EOT 0xff have no length */ - l = *p++; - if (!l) { - lwsl_err("%s: zero length\n", - __func__); - goto broken; - } - if (p + l > end) { - /* ...nice try... */ - lwsl_err("%s: bad len\n", - __func__); - goto broken; - } - } - - if (c == 0xff) /* end of options */ - break; - - m = 0; - switch (c) { - case LWSDHCPOPT_SUBNET_MASK: - n = IPV4_SUBNET_MASK; - goto get_ipv4; - - case LWSDHCPOPT_ROUTER: - n = IPV4_ROUTER; - goto get_ipv4; - - case LWSDHCPOPT_TIME_SERVER: - n = IPV4_TIME_SERVER; - goto get_ipv4; - - case LWSDHCPOPT_BROADCAST_ADS: - n = IPV4_BROADCAST; - goto get_ipv4; - - case LWSDHCPOPT_LEASE_TIME: - n = IPV4_LEASE_SECS; - goto get_ipv4; - - case LWSDHCPOPT_RENEWAL_TIME: /* AKA T1 */ - n = IPV4_RENEWAL_SECS; - goto get_ipv4; - - case LWSDHCPOPT_REBINDING_TIME: /* AKA T2 */ - n = IPV4_REBINDING_SECS; - goto get_ipv4; - - case LWSDHCPOPT_DNSERVER: - if (l & 3) - break; - m = IPV4_DNS_SRV_1; - while (l && m - IPV4_DNS_SRV_1 < 4) { - r->ipv4[m++] = lws_ser_ru32be(p); - l = (uint8_t)(l - 4); - p += 4; - } - break; - case LWSDHCPOPT_DOMAIN_NAME: - m = l; - if (m > (int)sizeof(r->domain) - 1) - m = sizeof(r->domain) - 1; - memcpy(r->domain, p, (unsigned int)m); - r->domain[m] = '\0'; - break; - - case LWSDHCPOPT_MESSAGE_TYPE: - /* - * Confirm this is the right message - * for the state of the negotiation - */ - if (r->state == LDHC_INIT && - *p != LWSDHCPOFFER) - goto broken; - if (r->state == LDHC_REQUESTING && - *p != LWSDHCPACK) - goto broken; - break; - - default: - break; - } - - p += l; - continue; -get_ipv4: - if (l >= 4) - r->ipv4[n] = lws_ser_ru32be(p); - p += l; - continue; -broken: - memset(r->ipv4, 0, sizeof(r->ipv4)); - break; - } - -#if defined(_DEBUG) - /* dump what we have parsed out */ - - for (n = 0; n < (int)LWS_ARRAY_SIZE(dhcp_entry_names); - n++) - if (n >= IPV4_LEASE_SECS) - lwsl_info("%s: %s: %ds\n", __func__, - dhcp_entry_names[n], - r->ipv4[n]); - else { - m = (int)ntohl(r->ipv4[n]); - lws_write_numeric_address((uint8_t *)&m, - 4,(char *)pkt, 20); - lwsl_info("%s: %s: %s\n", __func__, - dhcp_entry_names[n], - pkt); - } -#endif - - /* - * Having seen everything in there... do we really feel - * we could use it? Everything critical is there? - */ - - if (!r->ipv4[IPV4_PROPOSED] || - !r->ipv4[IPV4_SERVER] || - !r->ipv4[IPV4_ROUTER] || - !r->ipv4[IPV4_SUBNET_MASK] || - !r->ipv4[IPV4_LEASE_SECS] || - !r->ipv4[IPV4_DNS_SRV_1]) { - memset(r->ipv4, 0, sizeof(r->ipv4)); - break; - } - - /* - * Network layout has to be internally consistent... - * DHCP server has to be reachable by broadcast and - * default route has to be on same subnet - */ - - if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) != - (r->ipv4[IPV4_SERVER] & r->ipv4[IPV4_SUBNET_MASK])) - break; - - if ((r->ipv4[IPV4_PROPOSED] & r->ipv4[IPV4_SUBNET_MASK]) != - (r->ipv4[IPV4_ROUTER] & r->ipv4[IPV4_SUBNET_MASK])) - break; - - if (r->state == LDHC_INIT) { - lwsl_info("%s: moving to REQ\n", __func__); - r->state = LDHC_REQUESTING; - lws_callback_on_writable(r->wsi_raw); - break; - } - - /* - * that's it... commit to the configuration - */ - - /* set up our network interface as offered */ - - if (lws_plat_ifconfig_ip((const char *)&r[1], - r->wsi_raw->desc.sockfd, - (uint8_t *)&r->ipv4[IPV4_PROPOSED], - (uint8_t *)&r->ipv4[IPV4_SUBNET_MASK], - (uint8_t *)&r->ipv4[IPV4_ROUTER])) { - /* - * Problem setting the IP... maybe something - * transient like racing with NetworkManager? - * Since the sul retries are still around it - * will retry - */ - return -1; - } - - /* clear timeouts related to the broadcast socket */ - - lws_sul_cancel(&r->sul_write); - lws_sul_cancel(&r->sul_conn); - - lwsl_notice("%s: DHCP configured %s\n", __func__, - (const char *)&r[1]); - r->state = LDHC_BOUND; - - lws_state_transition_steps(&wsi->a.context->mgr_system, - LWS_SYSTATE_OPERATIONAL); - - r->cb(r->opaque, r->af, - (uint8_t *)&r->ipv4[IPV4_PROPOSED], 4); - - r->wsi_raw = NULL; - return -1; /* close the broadcast wsi */ - default: - break; - } - - break; - - case LWS_CALLBACK_RAW_WRITEABLE: - - if (!r) - break; - - /* - * UDP is not reliable, it can be locally dropped, or dropped - * by any intermediary or the remote peer. So even though we - * will do the write in a moment, we schedule another request - * for rewrite according to the wsi retry policy. - * - * If the result came before, we'll cancel it in the close flow. - * - * If we have already reached the end of our concealed retries - * in the policy, just close without another write. - */ - if (lws_dll2_is_detached(&r->sul_write.list) && - lws_retry_sul_schedule_retry_wsi(wsi, &r->sul_write, - lws_dhcpc_retry_write, - &r->retry_count_write)) { - /* we have reached the end of our concealed retries */ - lwsl_warn("%s: concealed retries done, failing\n", - __func__); - goto retry_conn; - } - - switch (r->state) { - case LDHC_INIT: - n = LWSDHCPDISCOVER; - goto bcast; - - case LDHC_REQUESTING: - n = LWSDHCPREQUEST; - - /* fallthru */ -bcast: - n = lws_dhcpc_prep(p + 28, (unsigned int) - (sizeof(pkt) - LWS_PRE - 28), r, n); - if (n < 0) { - lwsl_err("%s: failed to prep\n", __func__); - break; - } - - m = lws_plat_rawudp_broadcast(p, rawdisc, - LWS_ARRAY_SIZE(rawdisc), - (size_t)(n + 28), - r->wsi_raw->desc.sockfd, - (const char *)&r[1]); - if (m < 0) - lwsl_err("%s: Failed to write dhcp client req: " - "%d %d, errno %d\n", __func__, - n, m, LWS_ERRNO); - break; - default: - break; - } - - return 0; - -retry_conn: - lws_retry_sul_schedule(wsi->a.context, 0, &r->sul_conn, &bo2, - lws_dhcpc_retry_conn, - &r->retry_count_conn); - - return -1; - - default: - break; - } - - return 0; -} - -struct lws_protocols lws_system_protocol_dhcpc = - { "lws-dhcpclient", callback_dhcpc, 0, 128, }; - static void lws_dhcpc_destroy(lws_dhcpc_req_t **pr) { @@ -641,6 +43,8 @@ lws_dhcpc_destroy(lws_dhcpc_req_t **pr) lws_sul_cancel(&r->sul_conn); lws_sul_cancel(&r->sul_write); + lws_sul_cancel(&r->sul_renew); + if (r->wsi_raw) lws_set_timeout(r->wsi_raw, 1, LWS_TO_KILL_ASYNC); @@ -659,10 +63,8 @@ lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46) if (r->state == LDHC_BOUND) { if (sa46) { - memset(sa46, 0, sizeof(*sa46)); - sa46->sa4.sin_family = AF_INET; - sa46->sa4.sin_addr.s_addr = - r->ipv4[IPV4_DNS_SRV_1]; + memcpy(sa46, &r->is.sa46[LWSDH_SA46_DNS_SRV_1], + sizeof(*sa46)); } return 1; } @@ -721,9 +123,11 @@ lws_dhcpc_request(struct lws_context *context, const char *iface, int af, r->context = context; r->state = LDHC_INIT; + lws_strncpy(r->is.ifname, iface, sizeof(r->is.ifname)); + lws_dll2_add_head(&r->list, &context->dhcpc_owner); /* add him to list */ - lws_dhcpc_retry_conn(&r->sul_conn); + lws_dhcpc4_retry_conn(&r->sul_conn); return 0; } diff --git a/lib/system/dhcpclient/private-lib-system-dhcpclient.h b/lib/system/dhcpclient/private-lib-system-dhcpclient.h index ec03aae01..3d278ef9e 100644 --- a/lib/system/dhcpclient/private-lib-system-dhcpclient.h +++ b/lib/system/dhcpclient/private-lib-system-dhcpclient.h @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2019 Andy Green + * Copyright (C) 2010 - 2021 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -22,4 +22,91 @@ * IN THE SOFTWARE. */ +typedef enum { + LDHC_INIT_REBOOT, + LDHC_REBOOTING, /* jitterwait */ + LDHC_INIT, /* issue DHCPDISCOVER */ + LDHC_SELECTING, + LDHC_REQUESTING, + LDHC_REBINDING, + LDHC_BOUND, + LDHC_RENEWING +} lws_dhcpc_state_t; +enum { + LWSDHC4PDISCOVER = 1, + LWSDHC4POFFER, + LWSDHC4PREQUEST, + LWSDHC4PDECLINE, + LWSDHC4PACK, + LWSDHC4PNACK, + LWSDHC4PRELEASE, + + LWSDHC4POPT_PAD = 0, + LWSDHC4POPT_SUBNET_MASK = 1, + LWSDHC4POPT_TIME_OFFSET = 2, + LWSDHC4POPT_ROUTER = 3, + LWSDHC4POPT_TIME_SERVER = 4, + LWSDHC4POPT_NAME_SERVER = 5, + LWSDHC4POPT_DNSERVER = 6, + LWSDHC4POPT_LOG_SERVER = 7, + LWSDHC4POPT_COOKIE_SERVER = 8, + LWSDHC4POPT_LPR_SERVER = 9, + LWSDHC4POPT_IMPRESS_SERVER = 10, + LWSDHC4POPT_RESLOC_SERVER = 11, + LWSDHC4POPT_HOST_NAME = 12, + LWSDHC4POPT_BOOTFILE_SIZE = 13, + LWSDHC4POPT_MERIT_DUMP_FILE = 14, + LWSDHC4POPT_DOMAIN_NAME = 15, + LWSDHC4POPT_SWAP_SERVER = 16, + LWSDHC4POPT_ROOT_PATH = 17, + LWSDHC4POPT_EXTENSIONS_PATH = 18, + LWSDHC4POPT_BROADCAST_ADS = 28, + + LWSDHC4POPT_REQUESTED_ADS = 50, + LWSDHC4POPT_LEASE_TIME = 51, + LWSDHC4POPT_OPTION_OVERLOAD = 52, + LWSDHC4POPT_MESSAGE_TYPE = 53, + LWSDHC4POPT_SERVER_ID = 54, + LWSDHC4POPT_PARAM_REQ_LIST = 55, + LWSDHC4POPT_MESSAGE = 56, + LWSDHC4POPT_MAX_DHCP_MSG_SIZE = 57, + LWSDHC4POPT_RENEWAL_TIME = 58, /* AKA T1 */ + LWSDHC4POPT_REBINDING_TIME = 59, /* AKA T2 */ + LWSDHC4POPT_VENDOR_CLASS_ID = 60, + LWSDHC4POPT_CLIENT_ID = 61, + + LWSDHC4POPT_END_OPTIONS = 255 +}; + +typedef struct lws_dhcpc_req { + lws_dll2_t list; + + struct lws_context *context; + lws_sorted_usec_list_t sul_renew; + lws_sorted_usec_list_t sul_conn; + lws_sorted_usec_list_t sul_write; + dhcpc_cb_t cb; /* cb on completion / failure */ + void *opaque; /* ignored by lws, give to cb */ + + /* these are separated so we can close the bcast one asynchronously */ + struct lws *wsi_raw; /* for broadcast */ + lws_dhcpc_state_t state; + + lws_dhcpc_ifstate_t is; + + uint16_t retry_count_conn; + uint16_t retry_count_write; + uint8_t xid[4]; + uint8_t af; /* address family */ +} lws_dhcpc_req_t; +/* interface name is overallocated here */ + +void +lws_dhcpc4_retry_conn(struct lws_sorted_usec_list *sul); + +int +lws_dhcpc4_parse(lws_dhcpc_req_t *r, void *in, size_t len); + +void +lws_dhcpc_retry_write(struct lws_sorted_usec_list *sul); diff --git a/minimal-examples/api-tests/api-test-dhcpc/main.c b/minimal-examples/api-tests/api-test-dhcpc/main.c index 793c1fb43..3914f4397 100644 --- a/minimal-examples/api-tests/api-test-dhcpc/main.c +++ b/minimal-examples/api-tests/api-test-dhcpc/main.c @@ -14,10 +14,30 @@ static int interrupted, ok, fail, exp = 1; struct lws_context *context; const char *nif; +static const char * const sa46_names[] = { + "LWSDH_SA46_IP", + "LWSDH_SA46_DNS_SRV_1", + "LWSDH_SA46_DNS_SRV_2", + "LWSDH_SA46_DNS_SRV_3", + "LWSDH_SA46_DNS_SRV_4", + "LWSDH_SA46_IPV4_ROUTER", + "LWSDH_SA46_NTP_SERVER", + "LWSDH_SA46_DHCP_SERVER", +}; + static int -lws_dhcpc_cb(void *opaque, int af, uint8_t *ip, int ip_len) +lws_dhcpc_cb(void *opaque, lws_dhcpc_ifstate_t *is) { + unsigned int n; + char buf[64]; + lwsl_user("%s: dhcp set OK\n", __func__); + + for (n = 0; n < LWS_ARRAY_SIZE(sa46_names); n++) { + lws_sa46_write_numeric_address(&is->sa46[n], buf, sizeof(buf)); + lwsl_notice("%s: %s: %s\n", __func__, sa46_names[n], buf); + } + ok = 1; interrupted = 1; return 0;