1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_shell);
9
10 #include <stdint.h>
11 #include <zephyr/net/dhcpv4_server.h>
12 #include <zephyr/net/socket.h>
13
14 #include "net_shell_private.h"
15
cmd_net_dhcpv4_server_start(const struct shell * sh,size_t argc,char * argv[])16 static int cmd_net_dhcpv4_server_start(const struct shell *sh, size_t argc, char *argv[])
17 {
18 #if defined(CONFIG_NET_DHCPV4_SERVER)
19 struct net_if *iface = NULL;
20 struct in_addr base_addr;
21 int idx, ret;
22
23 idx = get_iface_idx(sh, argv[1]);
24 if (idx < 0) {
25 return -ENOEXEC;
26 }
27
28 iface = net_if_get_by_index(idx);
29 if (!iface) {
30 PR_WARNING("No such interface in index %d\n", idx);
31 return -ENOEXEC;
32 }
33
34 if (net_addr_pton(AF_INET, argv[2], &base_addr)) {
35 PR_ERROR("Invalid address: %s\n", argv[2]);
36 return -EINVAL;
37 }
38
39 ret = net_dhcpv4_server_start(iface, &base_addr);
40 if (ret == -EALREADY) {
41 PR_WARNING("DHCPv4 server already running on interface %d\n", idx);
42 } else if (ret < 0) {
43 PR_ERROR("DHCPv4 server failed to start on interface %d, error %d\n",
44 idx, -ret);
45 } else {
46 PR("DHCPv4 server started on interface %d\n", idx);
47 }
48 #else /* CONFIG_NET_DHCPV4_SERVER */
49 PR_INFO("Set %s to enable %s support.\n",
50 "CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
51 #endif /* CONFIG_NET_DHCPV4_SERVER */
52 return 0;
53 }
54
cmd_net_dhcpv4_server_stop(const struct shell * sh,size_t argc,char * argv[])55 static int cmd_net_dhcpv4_server_stop(const struct shell *sh, size_t argc, char *argv[])
56 {
57 #if defined(CONFIG_NET_DHCPV4_SERVER)
58 struct net_if *iface = NULL;
59 int idx, ret;
60
61 idx = get_iface_idx(sh, argv[1]);
62 if (idx < 0) {
63 return -ENOEXEC;
64 }
65
66 iface = net_if_get_by_index(idx);
67 if (!iface) {
68 PR_WARNING("No such interface in index %d\n", idx);
69 return -ENOEXEC;
70 }
71
72 ret = net_dhcpv4_server_stop(iface);
73 if (ret == -ENOENT) {
74 PR_WARNING("DHCPv4 server is not running on interface %d\n", idx);
75 } else if (ret < 0) {
76 PR_ERROR("DHCPv4 server failed to stop on interface %d, error %d\n",
77 idx, -ret);
78 } else {
79 PR("DHCPv4 server stopped on interface %d\n", idx);
80 }
81 #else /* CONFIG_NET_DHCPV4_SERVER */
82 PR_INFO("Set %s to enable %s support.\n",
83 "CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
84 #endif /* CONFIG_NET_DHCPV4_SERVER */
85 return 0;
86 }
87
88 #if defined(CONFIG_NET_DHCPV4_SERVER)
dhcpv4_addr_state_to_str(enum dhcpv4_server_addr_state state)89 static const char *dhcpv4_addr_state_to_str(enum dhcpv4_server_addr_state state)
90 {
91 switch (state) {
92 case DHCPV4_SERVER_ADDR_FREE:
93 return "FREE";
94 case DHCPV4_SERVER_ADDR_RESERVED:
95 return "RESERVED";
96 case DHCPV4_SERVER_ADDR_ALLOCATED:
97 return "ALLOCATED";
98 case DHCPV4_SERVER_ADDR_DECLINED:
99 return "DECLINED";
100 }
101
102 return "<UNKNOWN>";
103 }
104
timepoint_to_s(k_timepoint_t timepoint)105 static uint32_t timepoint_to_s(k_timepoint_t timepoint)
106 {
107 k_timeout_t timeout = sys_timepoint_timeout(timepoint);
108
109 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
110 return 0;
111 }
112
113 if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
114 return UINT32_MAX;
115 }
116
117 return k_ticks_to_ms_floor64(timeout.ticks) / 1000;
118 }
119
dhcpv4_lease_cb(struct net_if * iface,struct dhcpv4_addr_slot * lease,void * user_data)120 static void dhcpv4_lease_cb(struct net_if *iface,
121 struct dhcpv4_addr_slot *lease,
122 void *user_data)
123 {
124 struct net_shell_user_data *data = user_data;
125 const struct shell *sh = data->sh;
126 int *count = data->user_data;
127 char expiry_str[] = "4294967295"; /* Lease time is uint32_t, so take
128 * theoretical max.
129 */
130 char iface_name[IFNAMSIZ] = "";
131
132 if (*count == 0) {
133 PR(" Iface Address\t State\tExpiry (sec)\n");
134 }
135
136 (*count)++;
137
138 (void)net_if_get_name(iface, iface_name, sizeof(iface_name));
139
140 if (lease->state == DHCPV4_SERVER_ADDR_DECLINED) {
141 snprintk(expiry_str, sizeof(expiry_str) - 1, "infinite");
142 } else {
143 snprintk(expiry_str, sizeof(expiry_str) - 1, "%u",
144 timepoint_to_s(lease->expiry));
145 }
146
147 PR("%2d. %6s %15s\t%9s\t%12s\n",
148 *count, iface_name, net_sprint_ipv4_addr(&lease->addr),
149 dhcpv4_addr_state_to_str(lease->state), expiry_str);
150 }
151 #endif /* CONFIG_NET_DHCPV4_SERVER */
152
cmd_net_dhcpv4_server_status(const struct shell * sh,size_t argc,char * argv[])153 static int cmd_net_dhcpv4_server_status(const struct shell *sh, size_t argc, char *argv[])
154 {
155 #if defined(CONFIG_NET_DHCPV4_SERVER)
156 struct net_shell_user_data user_data;
157 struct net_if *iface = NULL;
158 int idx = 0, ret;
159 int count = 0;
160
161 if (argc > 1) {
162 idx = get_iface_idx(sh, argv[1]);
163 if (idx < 0) {
164 return -ENOEXEC;
165 }
166
167 iface = net_if_get_by_index(idx);
168 if (!iface) {
169 PR_WARNING("No such interface in index %d\n", idx);
170 return -ENOEXEC;
171 }
172 }
173
174 user_data.sh = sh;
175 user_data.user_data = &count;
176
177 ret = net_dhcpv4_server_foreach_lease(iface, dhcpv4_lease_cb, &user_data);
178 if (ret == -ENOENT) {
179 PR_WARNING("DHCPv4 server is not running on interface %d\n", idx);
180 } else if (count == 0) {
181 PR("DHCPv4 server - no addresses assigned\n");
182 }
183 #else /* CONFIG_NET_DHCPV4_SERVER */
184 PR_INFO("Set %s to enable %s support.\n",
185 "CONFIG_NET_DHCPV4_SERVER", "DHCPv4 server");
186 #endif /* CONFIG_NET_DHCPV4_SERVER */
187 return 0;
188 }
189
190 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dhcpv4_server,
191 SHELL_CMD_ARG(start, NULL, "Start the DHCPv4 server operation on the interface.\n"
192 "'net dhcpv4 server start <index> <base address>'\n"
193 "<index> is the network interface index.\n"
194 "<base address> is the first address for the address pool.",
195 cmd_net_dhcpv4_server_start, 3, 0),
196 SHELL_CMD_ARG(stop, NULL, "Stop the DHCPv4 server operation on the interface.\n"
197 "'net dhcpv4 server stop <index>'\n"
198 "<index> is the network interface index.",
199 cmd_net_dhcpv4_server_stop, 2, 0),
200 SHELL_CMD_ARG(status, NULL, "Print the DHCPv4 server status on the interface.\n"
201 "'net dhcpv4 server status <index>'\n"
202 "<index> is the network interface index. Optional.",
203 cmd_net_dhcpv4_server_status, 1, 1),
204 SHELL_SUBCMD_SET_END
205 );
206
207 SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dhcpv4,
208 SHELL_CMD(server, &net_cmd_dhcpv4_server,
209 "DHCPv4 server service management.",
210 NULL),
211 SHELL_SUBCMD_SET_END
212 );
213
214 SHELL_SUBCMD_ADD((net), dhcpv4, &net_cmd_dhcpv4, "Manage DHPCv4 services.",
215 NULL, 1, 0);
216