2016-09-12 16:12:47 +02:00
|
|
|
/** @file
|
|
|
|
* @brief DHCPv4 client related functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2016-12-21 21:36:13 +01:00
|
|
|
* Copyright (c) 2017 ARM Ltd.
|
2016-09-12 16:12:47 +02:00
|
|
|
* Copyright (c) 2016 Intel Corporation
|
2018-11-22 23:07:41 +01:00
|
|
|
* Copyright (c) 2018 Vincent van der Locht
|
2016-09-12 16:12:47 +02:00
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-09-12 16:12:47 +02:00
|
|
|
*/
|
|
|
|
|
2018-11-30 11:54:56 +01:00
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(net_dhcpv4, CONFIG_NET_DHCPV4_LOG_LEVEL);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
|
|
|
#include <errno.h>
|
2016-12-21 21:36:13 +01:00
|
|
|
#include <inttypes.h>
|
2020-06-12 06:28:55 +02:00
|
|
|
#include <random/rand32.h>
|
2016-09-12 16:12:47 +02:00
|
|
|
#include <net/net_core.h>
|
2017-04-03 17:14:35 +02:00
|
|
|
#include <net/net_pkt.h>
|
2016-09-12 16:12:47 +02:00
|
|
|
#include <net/net_if.h>
|
2018-05-30 13:37:18 +02:00
|
|
|
#include <net/net_mgmt.h>
|
2016-09-12 16:12:47 +02:00
|
|
|
#include "net_private.h"
|
|
|
|
|
2017-06-30 16:46:01 +02:00
|
|
|
#include <net/udp.h>
|
|
|
|
#include "udp_internal.h"
|
2016-09-12 16:12:47 +02:00
|
|
|
#include <net/dhcpv4.h>
|
2017-09-01 22:18:53 +02:00
|
|
|
#include <net/dns_resolve.h>
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 09:43:41 +02:00
|
|
|
#include "dhcpv4.h"
|
2018-07-24 11:19:39 +02:00
|
|
|
#include "ipv4.h"
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2018-08-14 10:14:07 +02:00
|
|
|
#define PKT_WAIT_TIME K_SECONDS(1)
|
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
static sys_slist_t dhcpv4_ifaces;
|
|
|
|
static struct k_delayed_work timeout_work;
|
|
|
|
|
2018-07-24 09:43:41 +02:00
|
|
|
static struct net_mgmt_event_callback mgmt4_cb;
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2017-02-11 22:44:17 +01:00
|
|
|
/* RFC 1497 [17] */
|
2020-05-27 18:26:57 +02:00
|
|
|
static const uint8_t magic_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static const char *dhcpv4_msg_type_name(enum dhcpv4_msg_type msg_type)
|
|
|
|
__attribute__((unused));
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static const char *dhcpv4_msg_type_name(enum dhcpv4_msg_type msg_type)
|
2016-12-21 21:36:13 +01:00
|
|
|
{
|
|
|
|
static const char * const name[] = {
|
|
|
|
"discover",
|
|
|
|
"offer",
|
|
|
|
"request",
|
|
|
|
"decline",
|
|
|
|
"ack",
|
|
|
|
"nak",
|
|
|
|
"release",
|
|
|
|
"inform"
|
|
|
|
};
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(msg_type >= 1 && msg_type <= sizeof(name));
|
|
|
|
return name[msg_type - 1];
|
|
|
|
}
|
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
/* Add magic cookie to DCHPv4 messages */
|
2018-07-24 10:51:52 +02:00
|
|
|
static inline bool dhcpv4_add_cookie(struct net_pkt *pkt)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2019-02-20 10:01:57 +01:00
|
|
|
if (net_pkt_write(pkt, (void *)magic_cookie,
|
|
|
|
ARRAY_SIZE(magic_cookie))) {
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2017-02-11 11:02:58 +01:00
|
|
|
/* Add a an option with the form OPTION LENGTH VALUE. */
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_add_option_length_value(struct net_pkt *pkt, uint8_t option,
|
|
|
|
uint8_t size, const void *value)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2019-02-20 10:01:57 +01:00
|
|
|
if (net_pkt_write_u8(pkt, option) ||
|
|
|
|
net_pkt_write_u8(pkt, size) ||
|
|
|
|
net_pkt_write(pkt, value, size)) {
|
2016-09-12 16:12:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-11 11:02:58 +01:00
|
|
|
/* Add DHCPv4 message type */
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_add_msg_type(struct net_pkt *pkt, uint8_t type)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-07-24 10:51:52 +02:00
|
|
|
return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_MSG_TYPE,
|
|
|
|
1, &type);
|
2017-02-11 11:02:58 +01:00
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2017-02-11 11:02:58 +01:00
|
|
|
/* Add DHCPv4 minimum required options for server to reply.
|
|
|
|
* Can be added more if needed.
|
|
|
|
*/
|
2018-07-24 10:51:52 +02:00
|
|
|
static bool dhcpv4_add_req_options(struct net_pkt *pkt)
|
2017-02-11 11:02:58 +01:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint8_t data[3] = { DHCPV4_OPTIONS_SUBNET_MASK,
|
2018-11-29 17:08:57 +01:00
|
|
|
DHCPV4_OPTIONS_ROUTER,
|
|
|
|
DHCPV4_OPTIONS_DNS_SERVER };
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_REQ_LIST,
|
|
|
|
ARRAY_SIZE(data), data);
|
2017-02-11 11:02:58 +01:00
|
|
|
}
|
2016-09-22 11:46:52 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static bool dhcpv4_add_server_id(struct net_pkt *pkt,
|
|
|
|
const struct in_addr *addr)
|
2017-02-11 11:02:58 +01:00
|
|
|
{
|
2018-07-24 10:51:52 +02:00
|
|
|
return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_SERVER_ID,
|
|
|
|
4, addr->s4_addr);
|
2017-02-11 11:02:58 +01:00
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static bool dhcpv4_add_req_ipaddr(struct net_pkt *pkt,
|
|
|
|
const struct in_addr *addr)
|
2017-02-11 11:02:58 +01:00
|
|
|
{
|
2018-07-24 10:51:52 +02:00
|
|
|
return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_REQ_IPADDR,
|
|
|
|
4, addr->s4_addr);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-21 12:24:22 +01:00
|
|
|
#if defined(CONFIG_NET_HOSTNAME_ENABLE)
|
|
|
|
static bool dhcpv4_add_hostname(struct net_pkt *pkt,
|
|
|
|
const char *hostname, const size_t size)
|
|
|
|
{
|
|
|
|
return dhcpv4_add_option_length_value(pkt, DHCPV4_OPTIONS_HOST_NAME,
|
|
|
|
size, hostname);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
/* Add DHCPv4 Options end, rest of the message can be padded wit zeros */
|
2018-07-24 10:51:52 +02:00
|
|
|
static inline bool dhcpv4_add_end(struct net_pkt *pkt)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2019-02-20 10:01:57 +01:00
|
|
|
if (net_pkt_write_u8(pkt, DHCPV4_OPTIONS_END)) {
|
2018-08-14 10:14:07 +02:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
/* File is empty ATM */
|
|
|
|
static inline bool dhcpv4_add_file(struct net_pkt *pkt)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_memset(pkt, 0, SIZE_OF_FILE)) {
|
2018-08-14 10:14:07 +02:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
/* SNAME is empty ATM */
|
|
|
|
static inline bool dhcpv4_add_sname(struct net_pkt *pkt)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_memset(pkt, 0, SIZE_OF_SNAME)) {
|
2018-02-28 00:36:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-02-28 00:36:07 +01:00
|
|
|
return true;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
/* Create DHCPv4 message and add options as per message type */
|
2020-05-27 18:26:57 +02:00
|
|
|
static struct net_pkt *dhcpv4_create_message(struct net_if *iface, uint8_t type,
|
2018-11-29 17:08:57 +01:00
|
|
|
const struct in_addr *ciaddr,
|
2019-04-04 17:39:10 +02:00
|
|
|
const struct in_addr *src_addr,
|
2018-11-29 17:08:57 +01:00
|
|
|
const struct in_addr *server_addr,
|
|
|
|
bool server_id, bool requested_ip)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-11-29 17:08:57 +01:00
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
|
2019-04-04 17:39:10 +02:00
|
|
|
const struct in_addr *addr;
|
2018-11-29 17:08:57 +01:00
|
|
|
size_t size = DHCPV4_MESSAGE_SIZE;
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
2016-09-12 16:12:47 +02:00
|
|
|
struct dhcp_msg *msg;
|
2020-02-21 12:24:22 +01:00
|
|
|
#if defined(CONFIG_NET_HOSTNAME_ENABLE)
|
|
|
|
const char *hostname = net_hostname_get();
|
|
|
|
const size_t hostname_size = strlen(hostname);
|
|
|
|
#endif
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2019-04-04 17:39:10 +02:00
|
|
|
if (src_addr == NULL) {
|
|
|
|
addr = net_ipv4_unspecified_address();
|
|
|
|
} else {
|
|
|
|
addr = src_addr;
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (server_id) {
|
|
|
|
size += DHCPV4_OLV_MSG_SERVER_ID;
|
|
|
|
}
|
2018-07-24 11:19:39 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (requested_ip) {
|
|
|
|
size += DHCPV4_OLV_MSG_REQ_IPADDR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == DHCPV4_MSG_TYPE_DISCOVER) {
|
|
|
|
size += DHCPV4_OLV_MSG_REQ_LIST;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2020-02-21 12:24:22 +01:00
|
|
|
#if defined(CONFIG_NET_HOSTNAME_ENABLE)
|
|
|
|
if (hostname_size > 0) {
|
|
|
|
size += DHCPV4_OLV_MSG_HOST_NAME + hostname_size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt = net_pkt_alloc_with_buffer(iface, size, AF_INET,
|
|
|
|
IPPROTO_UDP, K_FOREVER);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
net_pkt_set_ipv4_ttl(pkt, 0xFF);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2019-04-04 17:39:10 +02:00
|
|
|
if (net_ipv4_create(pkt, addr, server_addr) ||
|
2018-11-29 17:08:57 +01:00
|
|
|
net_udp_create(pkt, htons(DHCPV4_CLIENT_PORT),
|
|
|
|
htons(DHCPV4_SERVER_PORT))) {
|
2016-09-12 16:12:47 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
(void)memset(msg, 0, sizeof(struct dhcp_msg));
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
msg->op = DHCPV4_MSG_BOOT_REQUEST;
|
2016-09-12 16:12:47 +02:00
|
|
|
msg->htype = HARDWARE_ETHERNET_TYPE;
|
2018-11-29 17:08:57 +01:00
|
|
|
msg->hlen = HARDWARE_ETHERNET_LEN;
|
|
|
|
msg->xid = htonl(iface->config.dhcpv4.xid);
|
2016-09-12 16:12:47 +02:00
|
|
|
msg->flags = htons(DHCPV4_MSG_BROADCAST);
|
|
|
|
|
2017-02-11 13:16:04 +01:00
|
|
|
if (ciaddr) {
|
|
|
|
/* The ciaddr field was zero'd out above, if we are
|
|
|
|
* asked to send a ciaddr then fill it in now
|
|
|
|
* otherwise leave it as all zeros.
|
|
|
|
*/
|
|
|
|
memcpy(msg->ciaddr, ciaddr, 4);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
memcpy(msg->chaddr, net_if_get_link_addr(iface)->addr,
|
|
|
|
net_if_get_link_addr(iface)->len);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_set_data(pkt, &dhcp_access)) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
if (!dhcpv4_add_sname(pkt) ||
|
|
|
|
!dhcpv4_add_file(pkt) ||
|
|
|
|
!dhcpv4_add_cookie(pkt) ||
|
|
|
|
!dhcpv4_add_msg_type(pkt, type)) {
|
2016-09-12 16:12:47 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if ((server_id &&
|
|
|
|
!dhcpv4_add_server_id(pkt, &iface->config.dhcpv4.server_id)) ||
|
|
|
|
(requested_ip &&
|
|
|
|
!dhcpv4_add_req_ipaddr(pkt, &iface->config.dhcpv4.requested_ip))) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == DHCPV4_MSG_TYPE_DISCOVER && !dhcpv4_add_req_options(pkt)) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2020-02-21 12:24:22 +01:00
|
|
|
#if defined(CONFIG_NET_HOSTNAME_ENABLE)
|
|
|
|
if (hostname_size > 0 &&
|
|
|
|
!dhcpv4_add_hostname(pkt, hostname, hostname_size)) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (!dhcpv4_add_end(pkt)) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_pkt_cursor_init(pkt);
|
|
|
|
|
2019-02-01 21:33:55 +01:00
|
|
|
net_ipv4_finalize(pkt, IPPROTO_UDP);
|
2018-11-29 17:08:57 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
return pkt;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
|
|
|
fail:
|
2018-11-29 17:08:57 +01:00
|
|
|
NET_DBG("Message creation failed");
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2018-11-29 17:08:57 +01:00
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare DHCPv4 Message request and send it to peer */
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint32_t dhcpv4_send_request(struct net_if *iface)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2017-02-11 13:23:23 +01:00
|
|
|
const struct in_addr *server_addr = net_ipv4_broadcast_address();
|
2017-02-11 13:16:04 +01:00
|
|
|
const struct in_addr *ciaddr = NULL;
|
2019-04-04 17:39:10 +02:00
|
|
|
const struct in_addr *src_addr = NULL;
|
2017-02-11 13:16:04 +01:00
|
|
|
bool with_server_id = false;
|
|
|
|
bool with_requested_ip = false;
|
2018-07-24 10:57:54 +02:00
|
|
|
struct net_pkt *pkt;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t timeout;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.xid++;
|
2016-12-21 21:39:25 +01:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 13:18:21 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_SELECTING:
|
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
/* Not possible */
|
2020-01-08 08:42:23 +01:00
|
|
|
NET_ASSERT(0, "Invalid state %s",
|
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2017-02-11 13:18:21 +01:00
|
|
|
break;
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
2017-02-11 13:16:04 +01:00
|
|
|
with_server_id = true;
|
|
|
|
with_requested_ip = true;
|
2017-02-11 13:18:21 +01:00
|
|
|
break;
|
|
|
|
case NET_DHCPV4_RENEWING:
|
2017-02-11 13:16:04 +01:00
|
|
|
/* Since we have an address populate the ciaddr field.
|
|
|
|
*/
|
2018-01-11 15:06:53 +01:00
|
|
|
ciaddr = &iface->config.dhcpv4.requested_ip;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2017-02-11 13:23:23 +01:00
|
|
|
/* UNICAST the DHCPREQUEST */
|
2019-04-04 17:39:10 +02:00
|
|
|
src_addr = ciaddr;
|
2018-01-11 15:06:53 +01:00
|
|
|
server_addr = &iface->config.dhcpv4.server_id;
|
2017-02-11 13:23:23 +01:00
|
|
|
|
2017-02-11 13:18:21 +01:00
|
|
|
/* RFC2131 4.4.5 Client MUST NOT include server
|
|
|
|
* identifier in the DHCPREQUEST.
|
|
|
|
*/
|
|
|
|
break;
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
|
|
|
/* Since we have an address populate the ciaddr field.
|
|
|
|
*/
|
2018-01-11 15:06:53 +01:00
|
|
|
ciaddr = &iface->config.dhcpv4.requested_ip;
|
2019-04-04 17:39:10 +02:00
|
|
|
src_addr = ciaddr;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
|
|
|
break;
|
2017-02-11 13:18:21 +01:00
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt = dhcpv4_create_message(iface, DHCPV4_MSG_TYPE_REQUEST,
|
2019-04-04 17:39:10 +02:00
|
|
|
ciaddr, src_addr, server_addr,
|
|
|
|
with_server_id, with_requested_ip);
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!pkt) {
|
2017-02-11 13:16:04 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_send_data(pkt) < 0) {
|
2016-09-12 16:12:47 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-07-25 09:52:23 +02:00
|
|
|
timeout = DHCPV4_INITIAL_RETRY_TIMEOUT << iface->config.dhcpv4.attempts;
|
2016-12-21 21:36:13 +01:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.attempts++;
|
2017-02-11 17:59:50 +01:00
|
|
|
|
2018-08-16 13:32:31 +02:00
|
|
|
NET_DBG("send request dst=%s xid=0x%x ciaddr=%s%s%s timeout=%us",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv4_addr(server_addr)),
|
2018-08-16 13:32:31 +02:00
|
|
|
iface->config.dhcpv4.xid,
|
2018-10-02 13:57:55 +02:00
|
|
|
ciaddr ?
|
|
|
|
log_strdup(net_sprint_ipv4_addr(ciaddr)) : "<unknown>",
|
2018-08-16 13:32:31 +02:00
|
|
|
with_server_id ? " +server-id" : "",
|
|
|
|
with_requested_ip ? " +requested-ip" : "",
|
|
|
|
timeout);
|
2017-02-11 17:59:50 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.timer_start = k_uptime_get();
|
|
|
|
iface->config.dhcpv4.request_time = timeout;
|
|
|
|
|
|
|
|
return timeout;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
|
|
|
fail:
|
2017-11-16 02:50:33 +01:00
|
|
|
if (pkt) {
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
2018-07-25 13:55:01 +02:00
|
|
|
|
|
|
|
return UINT32_MAX;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2016-12-21 17:40:51 +01:00
|
|
|
/* Prepare DHCPv4 Discover message and broadcast it */
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint32_t dhcpv4_send_discover(struct net_if *iface)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t timeout;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.xid++;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt = dhcpv4_create_message(iface, DHCPV4_MSG_TYPE_DISCOVER,
|
2019-04-04 17:39:10 +02:00
|
|
|
NULL, NULL, net_ipv4_broadcast_address(),
|
2018-11-29 17:08:57 +01:00
|
|
|
false, false);
|
2017-04-05 08:37:44 +02:00
|
|
|
if (!pkt) {
|
2016-09-12 16:12:47 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_send_data(pkt) < 0) {
|
2016-09-12 16:12:47 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
timeout = DHCPV4_INITIAL_RETRY_TIMEOUT << iface->config.dhcpv4.attempts;
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.attempts++;
|
2017-02-11 18:05:30 +01:00
|
|
|
|
2018-07-25 14:06:10 +02:00
|
|
|
NET_DBG("send discover xid=0x%x timeout=%us",
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.xid, timeout);
|
2017-02-11 18:05:30 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.timer_start = k_uptime_get();
|
|
|
|
iface->config.dhcpv4.request_time = timeout;
|
|
|
|
|
|
|
|
return timeout;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
|
|
|
fail:
|
2017-11-16 02:50:33 +01:00
|
|
|
if (pkt) {
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
2018-07-25 13:55:01 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
return iface->config.dhcpv4.xid %
|
2019-02-28 14:43:41 +01:00
|
|
|
(CONFIG_NET_DHCPV4_INITIAL_DELAY_MAX -
|
|
|
|
DHCPV4_INITIAL_DELAY_MIN) +
|
|
|
|
DHCPV4_INITIAL_DELAY_MIN;
|
2018-07-25 13:55:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static void dhcpv4_update_timeout_work(uint32_t timeout)
|
2018-07-25 13:55:01 +02:00
|
|
|
{
|
|
|
|
if (!k_delayed_work_remaining_get(&timeout_work) ||
|
2020-04-03 12:47:34 +02:00
|
|
|
(MSEC_PER_SEC * timeout) <
|
2018-07-25 13:55:01 +02:00
|
|
|
k_delayed_work_remaining_get(&timeout_work)) {
|
|
|
|
k_delayed_work_cancel(&timeout_work);
|
|
|
|
k_delayed_work_submit(&timeout_work, K_SECONDS(timeout));
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_enter_selecting(struct net_if *iface)
|
2017-02-11 18:08:43 +01:00
|
|
|
{
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.lease_time = 0U;
|
|
|
|
iface->config.dhcpv4.renewal_time = 0U;
|
|
|
|
iface->config.dhcpv4.rebinding_time = 0U;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_SELECTING;
|
2017-02-11 18:08:43 +01:00
|
|
|
NET_DBG("enter state=%s",
|
2018-01-11 15:06:53 +01:00
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2018-07-25 13:55:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_check_timeout(int64_t start, uint32_t time, int64_t timeout)
|
2018-07-25 13:55:01 +02:00
|
|
|
{
|
2020-04-03 12:47:34 +02:00
|
|
|
start += MSEC_PER_SEC * time;
|
2018-07-25 13:55:01 +02:00
|
|
|
if (start < 0) {
|
|
|
|
start = -start;
|
|
|
|
}
|
2017-02-11 18:08:43 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
if (start > timeout) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-02-11 18:08:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_request_timedout(struct net_if *iface, int64_t timeout)
|
2017-02-11 18:15:14 +01:00
|
|
|
{
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_check_timeout(iface->config.dhcpv4.timer_start,
|
|
|
|
iface->config.dhcpv4.request_time,
|
|
|
|
timeout);
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_renewal_timedout(struct net_if *iface, int64_t timeout)
|
2018-07-25 13:55:01 +02:00
|
|
|
{
|
|
|
|
if (!dhcpv4_check_timeout(iface->config.dhcpv4.timer_start,
|
|
|
|
iface->config.dhcpv4.renewal_time,
|
|
|
|
timeout)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_RENEWING;
|
2017-02-11 18:15:14 +01:00
|
|
|
NET_DBG("enter state=%s",
|
2018-01-11 15:06:53 +01:00
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
2017-02-11 18:15:14 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
return true;
|
2017-02-11 18:15:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static bool dhcpv4_rebinding_timedout(struct net_if *iface, int64_t timeout)
|
2017-02-11 13:16:04 +01:00
|
|
|
{
|
2018-07-25 13:55:01 +02:00
|
|
|
if (!dhcpv4_check_timeout(iface->config.dhcpv4.timer_start,
|
|
|
|
iface->config.dhcpv4.rebinding_time,
|
|
|
|
timeout)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_REBINDING;
|
|
|
|
NET_DBG("enter state=%s",
|
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
return true;
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
static void dhcpv4_enter_requesting(struct net_if *iface)
|
2017-02-11 13:16:04 +01:00
|
|
|
{
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_REQUESTING;
|
|
|
|
NET_DBG("enter state=%s",
|
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
dhcpv4_update_timeout_work(dhcpv4_send_request(iface));
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_enter_bound(struct net_if *iface)
|
2017-02-11 13:16:04 +01:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t renewal_time;
|
|
|
|
uint32_t rebinding_time;
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
renewal_time = iface->config.dhcpv4.renewal_time;
|
2017-02-11 13:16:04 +01:00
|
|
|
if (!renewal_time) {
|
|
|
|
/* The default renewal time rfc2131 4.4.5 */
|
2019-03-27 02:57:45 +01:00
|
|
|
renewal_time = iface->config.dhcpv4.lease_time / 2U;
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.renewal_time = renewal_time;
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
rebinding_time = iface->config.dhcpv4.rebinding_time;
|
2017-02-11 13:16:04 +01:00
|
|
|
if (!rebinding_time) {
|
|
|
|
/* The default rebinding time rfc2131 4.4.5 */
|
2019-03-27 02:57:45 +01:00
|
|
|
rebinding_time = iface->config.dhcpv4.lease_time * 875U / 1000;
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.rebinding_time = rebinding_time;
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_BOUND;
|
2018-07-25 14:06:10 +02:00
|
|
|
NET_DBG("enter state=%s renewal=%us rebinding=%us",
|
2018-01-11 15:06:53 +01:00
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state),
|
2017-02-11 13:16:04 +01:00
|
|
|
renewal_time, rebinding_time);
|
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.timer_start = k_uptime_get();
|
2019-02-11 18:14:19 +01:00
|
|
|
iface->config.dhcpv4.request_time = MIN(renewal_time, rebinding_time);
|
2017-02-11 13:16:04 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
dhcpv4_update_timeout_work(iface->config.dhcpv4.request_time);
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint32_t dhcph4_manage_timers(struct net_if *iface, int64_t timeout)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-07-25 13:55:01 +02:00
|
|
|
NET_DBG("iface %p state=%s", iface,
|
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
if (!dhcpv4_request_timedout(iface, timeout)) {
|
|
|
|
return iface->config.dhcpv4.request_time;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
|
|
|
break;
|
2016-12-21 21:41:51 +01:00
|
|
|
case NET_DHCPV4_INIT:
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_selecting(iface);
|
2018-07-25 13:55:01 +02:00
|
|
|
/* Fall through, as discover msg needs to be sent */
|
2017-02-11 17:40:34 +01:00
|
|
|
case NET_DHCPV4_SELECTING:
|
2016-09-12 16:12:47 +02:00
|
|
|
/* Failed to get OFFER message, send DISCOVER again */
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_send_discover(iface);
|
2017-02-11 17:40:34 +01:00
|
|
|
case NET_DHCPV4_REQUESTING:
|
2017-01-26 09:23:13 +01:00
|
|
|
/* Maximum number of renewal attempts failed, so start
|
2016-09-12 16:12:47 +02:00
|
|
|
* from the beginning.
|
|
|
|
*/
|
2018-01-11 15:06:53 +01:00
|
|
|
if (iface->config.dhcpv4.attempts >=
|
|
|
|
DHCPV4_MAX_NUMBER_OF_ATTEMPTS) {
|
2016-12-21 21:36:13 +01:00
|
|
|
NET_DBG("too many attempts, restart");
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_selecting(iface);
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_send_discover(iface);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
2017-02-11 17:59:50 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_send_request(iface);
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_BOUND:
|
2018-07-25 13:55:01 +02:00
|
|
|
if (dhcpv4_renewal_timedout(iface, timeout) ||
|
|
|
|
dhcpv4_rebinding_timedout(iface, timeout)) {
|
|
|
|
return dhcpv4_send_request(iface);
|
|
|
|
}
|
|
|
|
|
2019-02-11 18:14:19 +01:00
|
|
|
return MIN(iface->config.dhcpv4.renewal_time,
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.rebinding_time);
|
2017-02-11 17:40:34 +01:00
|
|
|
case NET_DHCPV4_RENEWING:
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
2018-01-11 15:06:53 +01:00
|
|
|
if (iface->config.dhcpv4.attempts >=
|
|
|
|
DHCPV4_MAX_NUMBER_OF_ATTEMPTS) {
|
2016-12-21 21:36:13 +01:00
|
|
|
NET_DBG("too many attempts, restart");
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
if (!net_if_ipv4_addr_rm(iface,
|
2018-01-11 15:06:53 +01:00
|
|
|
&iface->config.dhcpv4.requested_ip)) {
|
2016-09-12 16:12:47 +02:00
|
|
|
NET_DBG("Failed to remove addr from iface");
|
|
|
|
}
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2017-01-26 09:23:13 +01:00
|
|
|
/* Maximum number of renewal attempts failed, so start
|
2016-09-12 16:12:47 +02:00
|
|
|
* from the beginning.
|
|
|
|
*/
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_selecting(iface);
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_send_discover(iface);
|
2016-09-12 16:12:47 +02:00
|
|
|
} else {
|
2018-07-25 13:55:01 +02:00
|
|
|
return dhcpv4_send_request(iface);
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
2018-07-25 13:55:01 +02:00
|
|
|
}
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
return UINT32_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dhcpv4_timeout(struct k_work *work)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t timeout_update = UINT32_MAX - 1;
|
|
|
|
int64_t timeout = k_uptime_get();
|
2018-07-25 13:55:01 +02:00
|
|
|
struct net_if_dhcpv4 *current, *next;
|
|
|
|
|
|
|
|
ARG_UNUSED(work);
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&dhcpv4_ifaces, current, next, node) {
|
|
|
|
struct net_if *iface = CONTAINER_OF(
|
|
|
|
CONTAINER_OF(current, struct net_if_config, dhcpv4),
|
|
|
|
struct net_if, config);
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t next_timeout;
|
2018-07-25 13:55:01 +02:00
|
|
|
|
|
|
|
next_timeout = dhcph4_manage_timers(iface, timeout);
|
|
|
|
if (next_timeout < timeout_update) {
|
|
|
|
timeout_update = next_timeout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timeout_update != UINT32_MAX) {
|
|
|
|
NET_DBG("Waiting for %us", timeout_update);
|
|
|
|
|
|
|
|
k_delayed_work_submit(&timeout_work,
|
|
|
|
K_SECONDS(timeout_update));
|
2017-02-22 13:00:10 +01:00
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2017-01-26 09:23:13 +01:00
|
|
|
/* Parse DHCPv4 options and retrieve relavant information
|
2016-09-12 16:12:47 +02:00
|
|
|
* as per RFC 2132.
|
|
|
|
*/
|
2018-11-29 17:08:57 +01:00
|
|
|
static bool dhcpv4_parse_options(struct net_pkt *pkt,
|
|
|
|
struct net_if *iface,
|
|
|
|
enum dhcpv4_msg_type *msg_type)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t cookie[4];
|
|
|
|
uint8_t length;
|
|
|
|
uint8_t type;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read(pkt, cookie, sizeof(cookie)) ||
|
2018-11-29 17:08:57 +01:00
|
|
|
memcmp(magic_cookie, cookie, sizeof(magic_cookie))) {
|
2016-09-12 16:12:47 +02:00
|
|
|
NET_DBG("Incorrect magic cookie");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
while (!net_pkt_read_u8(pkt, &type)) {
|
2016-09-12 16:12:47 +02:00
|
|
|
if (type == DHCPV4_OPTIONS_END) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_end");
|
2018-11-29 17:08:57 +01:00
|
|
|
return true;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read_u8(pkt, &length)) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_ERR("option parsing, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
2016-12-23 19:59:25 +01:00
|
|
|
case DHCPV4_OPTIONS_SUBNET_MASK: {
|
|
|
|
struct in_addr netmask;
|
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 4U) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_ERR("options_subnet_mask, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read(pkt, netmask.s4_addr, length)) {
|
2016-12-23 19:59:25 +01:00
|
|
|
NET_ERR("options_subnet_mask, short packet");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-12-23 19:59:25 +01:00
|
|
|
}
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2016-12-23 19:59:25 +01:00
|
|
|
net_if_ipv4_set_netmask(iface, &netmask);
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_subnet_mask %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv4_addr(&netmask)));
|
2016-09-12 16:12:47 +02:00
|
|
|
break;
|
2016-12-23 19:59:25 +01:00
|
|
|
}
|
2016-12-19 12:56:57 +01:00
|
|
|
case DHCPV4_OPTIONS_ROUTER: {
|
|
|
|
struct in_addr router;
|
|
|
|
|
|
|
|
/* Router option may present 1 or more
|
|
|
|
* addresses for routers on the clients
|
|
|
|
* subnet. Routers should be listed in order
|
|
|
|
* of preference. Hence we choose the first
|
|
|
|
* and skip the rest.
|
|
|
|
*/
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length % 4 != 0U || length < 4) {
|
2016-12-19 12:56:57 +01:00
|
|
|
NET_ERR("options_router, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-12-19 12:56:57 +01:00
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read(pkt, router.s4_addr, 4) ||
|
2019-03-27 02:57:45 +01:00
|
|
|
net_pkt_skip(pkt, length - 4U)) {
|
2016-12-19 12:56:57 +01:00
|
|
|
NET_ERR("options_router, short packet");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-12-19 12:56:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("options_router: %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv4_addr(&router)));
|
2016-12-19 12:56:57 +01:00
|
|
|
net_if_ipv4_set_gw(iface, &router);
|
|
|
|
break;
|
|
|
|
}
|
2017-09-01 22:18:53 +02:00
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
|
|
case DHCPV4_OPTIONS_DNS_SERVER: {
|
2019-10-21 18:45:28 +02:00
|
|
|
int i;
|
|
|
|
struct dns_resolve_context *ctx;
|
2017-09-01 22:18:53 +02:00
|
|
|
struct sockaddr_in dns;
|
|
|
|
const struct sockaddr *dns_servers[] = {
|
|
|
|
(struct sockaddr *)&dns, NULL
|
|
|
|
};
|
2018-07-24 10:57:54 +02:00
|
|
|
int status;
|
2017-09-01 22:18:53 +02:00
|
|
|
|
|
|
|
/* DNS server option may present 1 or more
|
|
|
|
* addresses. Each 4 bytes in length. DNS
|
|
|
|
* servers should be listed in order
|
|
|
|
* of preference. Hence we choose the first
|
|
|
|
* and skip the rest.
|
|
|
|
*/
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length % 4 != 0U) {
|
2017-09-01 22:18:53 +02:00
|
|
|
NET_ERR("options_dns, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2017-09-01 22:18:53 +02:00
|
|
|
}
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(&dns, 0, sizeof(dns));
|
2018-11-29 17:08:57 +01:00
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read(pkt, dns.sin_addr.s4_addr, 4) ||
|
2019-03-27 02:57:45 +01:00
|
|
|
net_pkt_skip(pkt, length - 4U)) {
|
2017-09-01 22:18:53 +02:00
|
|
|
NET_ERR("options_dns, short packet");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2017-09-01 22:18:53 +02:00
|
|
|
}
|
|
|
|
|
2019-10-21 18:45:28 +02:00
|
|
|
ctx = dns_resolve_get_default();
|
|
|
|
for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
|
|
|
|
if (!ctx->queries[i].cb) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dns_resolve_cancel(ctx, ctx->queries[i].id);
|
|
|
|
}
|
|
|
|
dns_resolve_close(ctx);
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2019-10-21 18:45:28 +02:00
|
|
|
dns.sin_family = AF_INET;
|
|
|
|
status = dns_resolve_init(ctx, NULL, dns_servers);
|
2017-09-01 22:18:53 +02:00
|
|
|
if (status < 0) {
|
|
|
|
NET_DBG("options_dns, failed to set "
|
|
|
|
"resolve address: %d", status);
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2017-09-01 22:18:53 +02:00
|
|
|
}
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2017-09-01 22:18:53 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-12 16:12:47 +02:00
|
|
|
case DHCPV4_OPTIONS_LEASE_TIME:
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 4U) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_ERR("options_lease_time, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read_be32(
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt, &iface->config.dhcpv4.lease_time) ||
|
|
|
|
!iface->config.dhcpv4.lease_time) {
|
|
|
|
NET_ERR("options_lease_time, wrong value");
|
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_lease_time: %u",
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.lease_time);
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
break;
|
|
|
|
case DHCPV4_OPTIONS_RENEWAL:
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 4U) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_renewal, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read_be32(
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt, &iface->config.dhcpv4.renewal_time) ||
|
|
|
|
!iface->config.dhcpv4.renewal_time) {
|
|
|
|
NET_DBG("options_renewal, wrong value");
|
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_renewal: %u",
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.renewal_time);
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2017-02-11 13:16:04 +01:00
|
|
|
break;
|
|
|
|
case DHCPV4_OPTIONS_REBINDING:
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 4U) {
|
2017-02-11 13:16:04 +01:00
|
|
|
NET_DBG("options_rebinding, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read_be32(
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt,
|
|
|
|
&iface->config.dhcpv4.rebinding_time) ||
|
|
|
|
!iface->config.dhcpv4.rebinding_time) {
|
|
|
|
NET_DBG("options_rebinding, wrong value");
|
|
|
|
return false;
|
2017-02-11 13:16:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("options_rebinding: %u",
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.rebinding_time);
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
break;
|
|
|
|
case DHCPV4_OPTIONS_SERVER_ID:
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 4U) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_server_id, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:40:48 +01:00
|
|
|
if (net_pkt_read(
|
2018-11-29 17:08:57 +01:00
|
|
|
pkt,
|
|
|
|
iface->config.dhcpv4.server_id.s4_addr,
|
|
|
|
length)) {
|
|
|
|
NET_DBG("options_server_id, read err");
|
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_server_id: %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv4_addr(
|
|
|
|
&iface->config.dhcpv4.server_id)));
|
2016-09-12 16:12:47 +02:00
|
|
|
break;
|
2017-02-09 16:04:28 +01:00
|
|
|
case DHCPV4_OPTIONS_MSG_TYPE: {
|
2019-03-27 02:57:45 +01:00
|
|
|
if (length != 1U) {
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("options_msg_type, bad length");
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
if (net_pkt_read_u8(pkt, (uint8_t *)msg_type)) {
|
2018-11-29 17:08:57 +01:00
|
|
|
NET_DBG("options_msg_type, read err");
|
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2017-02-09 16:04:28 +01:00
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
default:
|
2016-12-19 12:56:31 +01:00
|
|
|
NET_DBG("option unknown: %d", type);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_skip(pkt, length)) {
|
|
|
|
NET_DBG("option unknown, skip err");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 12:29:30 +01:00
|
|
|
/* Invalid case: Options without DHCPV4_OPTIONS_END. */
|
2018-11-29 17:08:57 +01:00
|
|
|
return false;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static inline void dhcpv4_handle_msg_offer(struct net_if *iface)
|
2016-09-12 16:12:47 +02:00
|
|
|
{
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
2017-02-11 12:55:56 +01:00
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
|
|
|
case NET_DHCPV4_RENEWING:
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
2017-02-11 12:55:56 +01:00
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
break;
|
|
|
|
case NET_DHCPV4_SELECTING:
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_requesting(iface);
|
2017-02-11 12:55:56 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_handle_msg_ack(struct net_if *iface)
|
2017-02-11 12:55:56 +01:00
|
|
|
{
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
2017-02-11 12:55:56 +01:00
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_SELECTING:
|
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
break;
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
2017-02-11 13:16:04 +01:00
|
|
|
NET_INFO("Received: %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv4_addr(
|
|
|
|
&iface->config.dhcpv4.requested_ip)));
|
2018-07-25 09:52:23 +02:00
|
|
|
|
2017-02-11 13:16:04 +01:00
|
|
|
if (!net_if_ipv4_addr_add(iface,
|
2018-01-11 15:06:53 +01:00
|
|
|
&iface->config.dhcpv4.requested_ip,
|
2017-02-11 13:16:04 +01:00
|
|
|
NET_ADDR_DHCP,
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.lease_time)) {
|
2017-02-11 13:16:04 +01:00
|
|
|
NET_DBG("Failed to add IPv4 addr to iface %p", iface);
|
|
|
|
return;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_bound(iface);
|
2017-02-11 13:16:04 +01:00
|
|
|
break;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_RENEWING:
|
|
|
|
case NET_DHCPV4_REBINDING:
|
|
|
|
/* TODO: If the renewal is success, update only
|
|
|
|
* vlifetime on iface.
|
|
|
|
*/
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_bound(iface);
|
2017-02-11 12:55:56 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_handle_msg_nak(struct net_if *iface)
|
2017-02-09 22:43:40 +01:00
|
|
|
{
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
2017-02-09 22:43:40 +01:00
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_SELECTING:
|
|
|
|
case NET_DHCPV4_RENEWING:
|
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
break;
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
2017-02-09 22:43:40 +01:00
|
|
|
/* Restart the configuration process. */
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_enter_selecting(iface);
|
2017-02-09 22:43:40 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_handle_reply(struct net_if *iface,
|
2017-02-11 12:55:56 +01:00
|
|
|
enum dhcpv4_msg_type msg_type)
|
|
|
|
{
|
|
|
|
NET_DBG("state=%s msg=%s",
|
2018-01-11 15:06:53 +01:00
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state),
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_msg_type_name(msg_type));
|
2017-02-11 12:55:56 +01:00
|
|
|
|
|
|
|
switch (msg_type) {
|
|
|
|
case DHCPV4_MSG_TYPE_OFFER:
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_handle_msg_offer(iface);
|
2017-02-11 12:55:56 +01:00
|
|
|
break;
|
|
|
|
case DHCPV4_MSG_TYPE_ACK:
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_handle_msg_ack(iface);
|
2017-02-11 12:55:56 +01:00
|
|
|
break;
|
2017-02-09 22:43:40 +01:00
|
|
|
case DHCPV4_MSG_TYPE_NAK:
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_handle_msg_nak(iface);
|
2017-02-09 22:43:40 +01:00
|
|
|
break;
|
2017-02-11 12:55:56 +01:00
|
|
|
default:
|
|
|
|
NET_DBG("ignore message");
|
|
|
|
break;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum net_verdict net_dhcpv4_input(struct net_conn *conn,
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt,
|
2019-01-30 10:07:10 +01:00
|
|
|
union net_ip_header *ip_hdr,
|
|
|
|
union net_proto_header *proto_hdr,
|
2016-09-12 16:12:47 +02:00
|
|
|
void *user_data)
|
|
|
|
{
|
2018-11-29 17:08:57 +01:00
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(dhcp_access, struct dhcp_msg);
|
2018-07-24 10:57:54 +02:00
|
|
|
enum dhcpv4_msg_type msg_type = 0;
|
2016-09-12 16:12:47 +02:00
|
|
|
struct dhcp_msg *msg;
|
|
|
|
struct net_if *iface;
|
|
|
|
|
|
|
|
if (!conn) {
|
|
|
|
NET_DBG("Invalid connection");
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (!pkt) {
|
|
|
|
NET_DBG("Invalid packet");
|
2016-09-12 16:12:47 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
iface = net_pkt_iface(pkt);
|
2016-09-12 16:12:47 +02:00
|
|
|
if (!iface) {
|
2016-12-21 21:36:13 +01:00
|
|
|
NET_DBG("no iface");
|
2016-09-12 16:12:47 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2017-01-26 09:23:13 +01:00
|
|
|
/* If the message is not DHCP then continue passing to
|
2016-09-12 16:12:47 +02:00
|
|
|
* related handlers.
|
|
|
|
*/
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_get_len(pkt) < NET_IPV4UDPH_LEN + sizeof(struct dhcp_msg)) {
|
2016-09-12 16:12:47 +02:00
|
|
|
NET_DBG("Input msg is not related to DHCPv4");
|
|
|
|
return NET_CONTINUE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
|
|
|
|
|
|
|
if (net_pkt_skip(pkt, NET_IPV4UDPH_LEN)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
msg = (struct dhcp_msg *)net_pkt_get_data(pkt, &dhcp_access);
|
2018-11-29 17:08:57 +01:00
|
|
|
if (!msg) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2017-01-26 09:23:13 +01:00
|
|
|
NET_DBG("Received dhcp msg [op=0x%x htype=0x%x hlen=%u xid=0x%x "
|
2018-07-09 12:44:07 +02:00
|
|
|
"secs=%u flags=0x%x chaddr=%s",
|
2016-11-19 09:48:49 +01:00
|
|
|
msg->op, msg->htype, msg->hlen, ntohl(msg->xid),
|
2018-10-02 13:57:55 +02:00
|
|
|
msg->secs, msg->flags,
|
|
|
|
log_strdup(net_sprint_ll_addr(msg->chaddr, 6)));
|
2018-09-03 16:33:33 +02:00
|
|
|
NET_DBG(" ciaddr=%d.%d.%d.%d",
|
|
|
|
msg->ciaddr[0], msg->ciaddr[1], msg->ciaddr[2], msg->ciaddr[3]);
|
|
|
|
NET_DBG(" yiaddr=%d.%d.%d.%d",
|
2018-07-09 12:44:07 +02:00
|
|
|
msg->yiaddr[0], msg->yiaddr[1], msg->yiaddr[2], msg->yiaddr[3]);
|
2018-09-03 16:33:33 +02:00
|
|
|
NET_DBG(" siaddr=%d.%d.%d.%d",
|
|
|
|
msg->siaddr[0], msg->siaddr[1], msg->siaddr[2], msg->siaddr[3]);
|
|
|
|
NET_DBG(" giaddr=%d.%d.%d.%d]",
|
2018-07-09 12:44:07 +02:00
|
|
|
msg->giaddr[0], msg->giaddr[1], msg->giaddr[2], msg->giaddr[3]);
|
2016-10-05 16:05:11 +02:00
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
if (!(msg->op == DHCPV4_MSG_BOOT_REPLY &&
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.xid == ntohl(msg->xid) &&
|
|
|
|
!memcmp(msg->chaddr, net_if_get_link_addr(iface)->addr,
|
|
|
|
net_if_get_link_addr(iface)->len))) {
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2016-11-19 09:48:49 +01:00
|
|
|
NET_DBG("Unexpected op (%d), xid (%x vs %x) or chaddr",
|
2018-01-11 15:06:53 +01:00
|
|
|
msg->op, iface->config.dhcpv4.xid, ntohl(msg->xid));
|
2018-11-29 17:08:57 +01:00
|
|
|
return NET_DROP;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
memcpy(iface->config.dhcpv4.requested_ip.s4_addr,
|
|
|
|
msg->yiaddr, sizeof(msg->yiaddr));
|
|
|
|
|
|
|
|
net_pkt_acknowledge_data(pkt, &dhcp_access);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2017-01-26 09:23:13 +01:00
|
|
|
/* SNAME, FILE are not used at the moment, skip it */
|
2018-11-29 17:08:57 +01:00
|
|
|
if (net_pkt_skip(pkt, SIZE_OF_SNAME + SIZE_OF_FILE)) {
|
2016-12-21 21:36:13 +01:00
|
|
|
NET_DBG("short packet while skipping sname");
|
2018-11-29 17:08:57 +01:00
|
|
|
return NET_DROP;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2018-11-29 17:08:57 +01:00
|
|
|
if (!dhcpv4_parse_options(pkt, iface, &msg_type)) {
|
|
|
|
return NET_DROP;
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
dhcpv4_handle_reply(iface, msg_type);
|
2016-09-12 16:12:47 +02:00
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
static void dhcpv4_iface_event_handler(struct net_mgmt_event_callback *cb,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t mgmt_event, struct net_if *iface)
|
2018-05-30 13:37:18 +02:00
|
|
|
{
|
2018-11-22 23:07:41 +01:00
|
|
|
sys_snode_t *node = NULL;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_NODE(&dhcpv4_ifaces, node) {
|
|
|
|
if (node == &iface->config.dhcpv4.node) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-30 13:37:18 +02:00
|
|
|
if (mgmt_event == NET_EVENT_IF_DOWN) {
|
|
|
|
NET_DBG("Interface %p going down", iface);
|
|
|
|
|
|
|
|
if (iface->config.dhcpv4.state == NET_DHCPV4_BOUND) {
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
2018-05-30 13:37:18 +02:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_RENEWING;
|
|
|
|
NET_DBG("enter state=%s", net_dhcpv4_state_name(
|
|
|
|
iface->config.dhcpv4.state));
|
|
|
|
}
|
|
|
|
} else if (mgmt_event == NET_EVENT_IF_UP) {
|
|
|
|
NET_DBG("Interface %p coming up", iface);
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
/* We should not call dhcpv4_send_request() directly here as
|
2018-05-30 13:37:18 +02:00
|
|
|
* the CONFIG_NET_MGMT_EVENT_STACK_SIZE is not large
|
2018-07-25 13:55:01 +02:00
|
|
|
* enough. Instead we can force a request timeout
|
2018-07-24 10:51:52 +02:00
|
|
|
* which will then call dhcpv4_send_request() automatically.
|
2018-05-30 13:37:18 +02:00
|
|
|
*/
|
2018-07-25 13:55:01 +02:00
|
|
|
iface->config.dhcpv4.timer_start = k_uptime_get() - 1;
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.request_time = 0U;
|
2018-07-25 13:55:01 +02:00
|
|
|
|
|
|
|
k_delayed_work_cancel(&timeout_work);
|
|
|
|
k_delayed_work_submit(&timeout_work, K_NO_WAIT);
|
2018-05-30 13:37:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
const char *net_dhcpv4_state_name(enum net_dhcpv4_state state)
|
|
|
|
{
|
|
|
|
static const char * const name[] = {
|
|
|
|
"disabled",
|
|
|
|
"init",
|
|
|
|
"selecting",
|
|
|
|
"requesting",
|
|
|
|
"renewing",
|
|
|
|
"rebinding",
|
|
|
|
"bound",
|
|
|
|
};
|
|
|
|
|
|
|
|
__ASSERT_NO_MSG(state >= 0 && state < sizeof(name));
|
|
|
|
return name[state];
|
|
|
|
}
|
|
|
|
|
2016-09-12 16:12:47 +02:00
|
|
|
void net_dhcpv4_start(struct net_if *iface)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t timeout;
|
|
|
|
uint32_t entropy;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_INIT;
|
2018-07-25 13:55:01 +02:00
|
|
|
NET_DBG("iface %p state=%s", iface,
|
2018-01-11 15:06:53 +01:00
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2016-12-21 21:36:13 +01:00
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.attempts = 0U;
|
|
|
|
iface->config.dhcpv4.lease_time = 0U;
|
|
|
|
iface->config.dhcpv4.renewal_time = 0U;
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
iface->config.dhcpv4.server_id.s_addr = 0U;
|
|
|
|
iface->config.dhcpv4.requested_ip.s_addr = 0U;
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2017-02-11 18:45:17 +01:00
|
|
|
/* We need entropy for both an XID and a random delay
|
|
|
|
* before sending the initial discover message.
|
|
|
|
*/
|
|
|
|
entropy = sys_rand32_get();
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2017-02-11 18:45:17 +01:00
|
|
|
/* A DHCP client MUST choose xid's in such a way as to
|
|
|
|
* minimize the change of using and xid identical to
|
|
|
|
* one used by another client. Choose a random xid st
|
|
|
|
* startup and increment it on each new request.
|
|
|
|
*/
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.xid = entropy;
|
2016-09-12 16:12:47 +02:00
|
|
|
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2017-02-11 18:45:17 +01:00
|
|
|
/* RFC2131 4.1.1 requires we wait a random period
|
|
|
|
* between 1 and 10 seconds before sending the initial
|
|
|
|
* discover.
|
|
|
|
*/
|
|
|
|
timeout = entropy %
|
2019-02-28 14:43:41 +01:00
|
|
|
(CONFIG_NET_DHCPV4_INITIAL_DELAY_MAX -
|
|
|
|
DHCPV4_INITIAL_DELAY_MIN) +
|
|
|
|
DHCPV4_INITIAL_DELAY_MIN;
|
2017-02-11 18:45:17 +01:00
|
|
|
|
2018-07-25 14:06:10 +02:00
|
|
|
NET_DBG("wait timeout=%us", timeout);
|
2016-12-21 21:41:51 +01:00
|
|
|
|
2018-11-22 23:07:41 +01:00
|
|
|
if (sys_slist_is_empty(&dhcpv4_ifaces)) {
|
|
|
|
net_mgmt_add_event_callback(&mgmt4_cb);
|
|
|
|
}
|
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
sys_slist_append(&dhcpv4_ifaces,
|
|
|
|
&iface->config.dhcpv4.node);
|
|
|
|
|
|
|
|
iface->config.dhcpv4.timer_start = k_uptime_get();
|
|
|
|
iface->config.dhcpv4.request_time = timeout;
|
|
|
|
|
|
|
|
dhcpv4_update_timeout_work(timeout);
|
|
|
|
|
2017-02-11 18:45:17 +01:00
|
|
|
break;
|
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_SELECTING:
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
|
|
|
case NET_DHCPV4_RENEWING:
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_dhcpv4_stop(struct net_if *iface)
|
|
|
|
{
|
2018-01-11 15:06:53 +01:00
|
|
|
switch (iface->config.dhcpv4.state) {
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_DISABLED:
|
|
|
|
break;
|
|
|
|
|
2018-11-25 23:44:56 +01:00
|
|
|
case NET_DHCPV4_RENEWING:
|
2017-02-13 22:46:21 +01:00
|
|
|
case NET_DHCPV4_BOUND:
|
|
|
|
if (!net_if_ipv4_addr_rm(iface,
|
2018-01-11 15:06:53 +01:00
|
|
|
&iface->config.dhcpv4.requested_ip)) {
|
2017-02-13 22:46:21 +01:00
|
|
|
NET_DBG("Failed to remove addr from iface");
|
|
|
|
}
|
|
|
|
|
2018-07-25 09:52:23 +02:00
|
|
|
/* Fall through */
|
2017-02-11 18:45:17 +01:00
|
|
|
case NET_DHCPV4_INIT:
|
|
|
|
case NET_DHCPV4_SELECTING:
|
|
|
|
case NET_DHCPV4_REQUESTING:
|
2017-02-11 13:16:04 +01:00
|
|
|
case NET_DHCPV4_REBINDING:
|
2018-01-11 15:06:53 +01:00
|
|
|
iface->config.dhcpv4.state = NET_DHCPV4_DISABLED;
|
|
|
|
NET_DBG("state=%s",
|
|
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
2017-02-11 18:45:17 +01:00
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
sys_slist_find_and_remove(&dhcpv4_ifaces,
|
|
|
|
&iface->config.dhcpv4.node);
|
|
|
|
|
|
|
|
if (sys_slist_is_empty(&dhcpv4_ifaces)) {
|
|
|
|
k_delayed_work_cancel(&timeout_work);
|
2018-11-22 23:07:41 +01:00
|
|
|
net_mgmt_del_event_callback(&mgmt4_cb);
|
2018-07-25 13:55:01 +02:00
|
|
|
}
|
|
|
|
|
2017-02-11 18:45:17 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-09-12 16:12:47 +02:00
|
|
|
}
|
2017-02-22 13:36:31 +01:00
|
|
|
|
2018-07-24 10:51:52 +02:00
|
|
|
int net_dhcpv4_init(void)
|
2017-02-22 13:36:31 +01:00
|
|
|
{
|
2017-05-19 10:50:40 +02:00
|
|
|
struct sockaddr local_addr;
|
2017-02-22 13:36:31 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
NET_DBG("");
|
|
|
|
|
2017-05-19 10:50:40 +02:00
|
|
|
net_ipaddr_copy(&net_sin(&local_addr)->sin_addr,
|
|
|
|
net_ipv4_unspecified_address());
|
2017-08-18 09:03:46 +02:00
|
|
|
local_addr.sa_family = AF_INET;
|
2017-05-19 10:50:40 +02:00
|
|
|
|
2017-02-22 13:36:31 +01:00
|
|
|
/* Register UDP input callback on
|
|
|
|
* DHCPV4_SERVER_PORT(67) and DHCPV4_CLIENT_PORT(68) for
|
|
|
|
* all dhcpv4 related incoming packets.
|
|
|
|
*/
|
2019-01-28 14:31:33 +01:00
|
|
|
ret = net_udp_register(AF_INET, NULL, &local_addr,
|
2017-02-22 13:36:31 +01:00
|
|
|
DHCPV4_SERVER_PORT,
|
|
|
|
DHCPV4_CLIENT_PORT,
|
|
|
|
net_dhcpv4_input, NULL, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("UDP callback registration failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-25 13:55:01 +02:00
|
|
|
k_delayed_work_init(&timeout_work, dhcpv4_timeout);
|
|
|
|
|
2018-11-22 23:07:41 +01:00
|
|
|
/* Catch network interface UP or DOWN events and renew the address
|
|
|
|
* if interface is coming back up again.
|
|
|
|
*/
|
|
|
|
net_mgmt_init_event_callback(&mgmt4_cb, dhcpv4_iface_event_handler,
|
|
|
|
NET_EVENT_IF_DOWN | NET_EVENT_IF_UP);
|
|
|
|
|
2017-02-22 13:36:31 +01:00
|
|
|
return 0;
|
|
|
|
}
|