Skip to content

Commit

Permalink
[nrf fromtree] net: shell: Implement DHCPv4 server shell commands
Browse files Browse the repository at this point in the history
Implement DHCPv4 shell module, which allows to start/stop DHCPv4 server
operation, and print server status (address leases).

Signed-off-by: Robert Lubos <[email protected]>
(cherry picked from commit 2c70c5d)
  • Loading branch information
rlubos committed Jan 23, 2024
1 parent 7a0cbb6 commit d97e4bc
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
1 change: 1 addition & 0 deletions subsys/net/lib/shell/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ zephyr_library_sources(allocs.c)
zephyr_library_sources(arp.c)
zephyr_library_sources(capture.c)
zephyr_library_sources(conn.c)
zephyr_library_sources(dhcpv4.c)
zephyr_library_sources(dns.c)
zephyr_library_sources(events.c)
zephyr_library_sources(gptp.c)
Expand Down
215 changes: 215 additions & 0 deletions subsys/net/lib/shell/dhcpv4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* 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);

0 comments on commit d97e4bc

Please sign in to comment.