dhcp: clean
This commit is contained in:
parent
d86d2884c8
commit
599691d233
|
@ -63,3 +63,4 @@ doc
|
|||
/b1/
|
||||
/destdir/
|
||||
/bb1/
|
||||
/bb3/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* 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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue