2c70c5d74a
Implement DHCPv4 shell module, which allows to start/stop DHCPv4 server operation, and print server status (address leases). Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
216 lines
5.7 KiB
C
216 lines
5.7 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_DECLARE(net_shell);
|
|
|
|
#include <stdint.h>
|
|
#include <zephyr/net/dhcpv4_server.h>
|
|
#include <zephyr/net/socket.h>
|
|
|
|
#include "net_shell_private.h"
|
|
|
|
static int cmd_net_dhcpv4_server_start(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_DHCPV4_SERVER)
|
|
struct net_if *iface = NULL;
|
|
struct in_addr base_addr;
|
|
int idx, ret;
|
|
|
|
idx = get_iface_idx(sh, argv[1]);
|
|
if (idx < 0) {
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
iface = net_if_get_by_index(idx);
|
|
if (!iface) {
|
|
PR_WARNING("No such interface in index %d\n", idx);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (net_addr_pton(AF_INET, argv[2], &base_addr)) {
|
|
PR_ERROR("Invalid address: %s\n", argv[2]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = net_dhcpv4_server_start(iface, &base_addr);
|
|
if (ret == -EALREADY) {
|
|
PR_WARNING("DHCPv4 server already running on interface %d\n", idx);
|
|
} else if (ret < 0) {
|
|
PR_ERROR("DHCPv4 server failed to start on interface %d, error %d\n",
|
|
idx, -ret);
|
|
} else {
|
|
PR("DHCPv4 server started on interface %d\n", idx);
|
|
}
|
|
#else /* CONFIG_NET_DHCPV4_SERVER */
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
|
|
#endif /* CONFIG_NET_DHCPV4_SERVER */
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_dhcpv4_server_stop(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_DHCPV4_SERVER)
|
|
struct net_if *iface = NULL;
|
|
int idx, ret;
|
|
|
|
idx = get_iface_idx(sh, argv[1]);
|
|
if (idx < 0) {
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
iface = net_if_get_by_index(idx);
|
|
if (!iface) {
|
|
PR_WARNING("No such interface in index %d\n", idx);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_dhcpv4_server_stop(iface);
|
|
if (ret == -ENOENT) {
|
|
PR_WARNING("DHCPv4 server is not running on interface %d\n", idx);
|
|
} else if (ret < 0) {
|
|
PR_ERROR("DHCPv4 server failed to stop on interface %d, error %d\n",
|
|
idx, -ret);
|
|
} else {
|
|
PR("DHCPv4 server stopped on interface %d\n", idx);
|
|
}
|
|
#else /* CONFIG_NET_DHCPV4_SERVER */
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
|
|
#endif /* CONFIG_NET_DHCPV4_SERVER */
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DHCPV4_SERVER)
|
|
static const char *dhcpv4_addr_state_to_str(enum dhcpv4_server_addr_state state)
|
|
{
|
|
switch (state) {
|
|
case DHCPV4_SERVER_ADDR_FREE:
|
|
return "FREE";
|
|
case DHCPV4_SERVER_ADDR_RESERVED:
|
|
return "RESERVED";
|
|
case DHCPV4_SERVER_ADDR_ALLOCATED:
|
|
return "ALLOCATED";
|
|
case DHCPV4_SERVER_ADDR_DECLINED:
|
|
return "DECLINED";
|
|
}
|
|
|
|
return "<UNKNOWN>";
|
|
}
|
|
|
|
static uint32_t timepoint_to_s(k_timepoint_t timepoint)
|
|
{
|
|
k_timeout_t timeout = sys_timepoint_timeout(timepoint);
|
|
|
|
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
|
|
return 0;
|
|
}
|
|
|
|
if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
|
|
return UINT32_MAX;
|
|
}
|
|
|
|
return k_ticks_to_ms_floor64(timeout.ticks) / 1000;
|
|
}
|
|
|
|
static void dhcpv4_lease_cb(struct net_if *iface,
|
|
struct dhcpv4_addr_slot *lease,
|
|
void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *sh = data->sh;
|
|
int *count = data->user_data;
|
|
char expiry_str[] = "4294967295"; /* Lease time is uint32_t, so take
|
|
* theoretical max.
|
|
*/
|
|
char iface_name[IFNAMSIZ] = "";
|
|
|
|
if (*count == 0) {
|
|
PR(" Iface Address\t State\tExpiry (sec)\n");
|
|
}
|
|
|
|
(*count)++;
|
|
|
|
(void)net_if_get_name(iface, iface_name, sizeof(iface_name));
|
|
|
|
if (lease->state == DHCPV4_SERVER_ADDR_DECLINED) {
|
|
snprintk(expiry_str, sizeof(expiry_str) - 1, "infinite");
|
|
} else {
|
|
snprintk(expiry_str, sizeof(expiry_str) - 1, "%u",
|
|
timepoint_to_s(lease->expiry));
|
|
}
|
|
|
|
PR("%2d. %6s %15s\t%9s\t%12s\n",
|
|
*count, iface_name, net_sprint_ipv4_addr(&lease->addr),
|
|
dhcpv4_addr_state_to_str(lease->state), expiry_str);
|
|
}
|
|
#endif /* CONFIG_NET_DHCPV4_SERVER */
|
|
|
|
static int cmd_net_dhcpv4_server_status(const struct shell *sh, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_DHCPV4_SERVER)
|
|
struct net_shell_user_data user_data;
|
|
struct net_if *iface = NULL;
|
|
int idx = 0, ret;
|
|
int count = 0;
|
|
|
|
if (argc > 1) {
|
|
idx = get_iface_idx(sh, argv[1]);
|
|
if (idx < 0) {
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
iface = net_if_get_by_index(idx);
|
|
if (!iface) {
|
|
PR_WARNING("No such interface in index %d\n", idx);
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
user_data.sh = sh;
|
|
user_data.user_data = &count;
|
|
|
|
ret = net_dhcpv4_server_foreach_lease(iface, dhcpv4_lease_cb, &user_data);
|
|
if (ret == -ENOENT) {
|
|
PR_WARNING("DHCPv4 server is not running on interface %d\n", idx);
|
|
} else if (count == 0) {
|
|
PR("DHCPv4 server - no addresses assigned\n");
|
|
}
|
|
#else /* CONFIG_NET_DHCPV4_SERVER */
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
|
|
#endif /* CONFIG_NET_DHCPV4_SERVER */
|
|
return 0;
|
|
}
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dhcpv4_server,
|
|
SHELL_CMD_ARG(start, NULL, "Start the DHCPv4 server operation on the interface.\n"
|
|
"'net dhcpv4 server start <index> <base address>'\n"
|
|
"<index> is the network interface index.\n"
|
|
"<base address> is the first address for the address pool.",
|
|
cmd_net_dhcpv4_server_start, 3, 0),
|
|
SHELL_CMD_ARG(stop, NULL, "Stop the DHCPv4 server operation on the interface.\n"
|
|
"'net dhcpv4 server stop <index>'\n"
|
|
"<index> is the network interface index.",
|
|
cmd_net_dhcpv4_server_stop, 2, 0),
|
|
SHELL_CMD_ARG(status, NULL, "Print the DHCPv4 server status on the interface.\n"
|
|
"'net dhcpv4 server status <index>'\n"
|
|
"<index> is the network interface index. Optional.",
|
|
cmd_net_dhcpv4_server_status, 1, 1),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dhcpv4,
|
|
SHELL_CMD(server, &net_cmd_dhcpv4_server,
|
|
"DHCPv4 server service management.",
|
|
NULL),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_SUBCMD_ADD((net), dhcpv4, &net_cmd_dhcpv4, "Manage DHPCv4 services.",
|
|
NULL, 1, 0);
|