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