blob: 32edca8b24bb008c7d8c0b852aa3b66f5fbc7dcc [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(net_shell);
#include <zephyr/kernel.h>
#include <zephyr/shell/shell_uart.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/net_event.h>
#include <zephyr/net/coap_mgmt.h>
#include "net_shell_private.h"
#if defined(CONFIG_NET_MGMT_EVENT_MONITOR)
#define EVENT_MON_STACK_SIZE 1024
#define THREAD_PRIORITY K_PRIO_COOP(2)
#define MAX_EVENT_INFO_SIZE NET_EVENT_INFO_MAX_SIZE
#define MONITOR_L2_MASK (_NET_EVENT_IF_BASE)
#define MONITOR_L3_IPV4_MASK (_NET_EVENT_IPV4_BASE)
#define MONITOR_L3_IPV6_MASK (_NET_EVENT_IPV6_BASE)
#define MONITOR_L4_MASK (_NET_EVENT_L4_BASE)
static bool net_event_monitoring;
static bool net_event_shutting_down;
static struct net_mgmt_event_callback l2_cb;
static struct net_mgmt_event_callback l3_ipv4_cb;
static struct net_mgmt_event_callback l3_ipv6_cb;
static struct net_mgmt_event_callback l4_cb;
static struct k_thread event_mon;
static K_THREAD_STACK_DEFINE(event_mon_stack, EVENT_MON_STACK_SIZE);
struct event_msg {
struct net_if *iface;
size_t len;
uint32_t event;
uint8_t data[MAX_EVENT_INFO_SIZE];
};
K_MSGQ_DEFINE(event_mon_msgq, sizeof(struct event_msg),
CONFIG_NET_MGMT_EVENT_QUEUE_SIZE, sizeof(intptr_t));
static void event_handler(struct net_mgmt_event_callback *cb,
uint32_t mgmt_event, struct net_if *iface)
{
struct event_msg msg;
int ret;
memset(&msg, 0, sizeof(msg));
msg.len = MIN(sizeof(msg.data), cb->info_length);
msg.event = mgmt_event;
msg.iface = iface;
if (cb->info_length > 0) {
memcpy(msg.data, cb->info, msg.len);
}
ret = k_msgq_put(&event_mon_msgq, (void *)&msg, K_MSEC(10));
if (ret < 0) {
NET_ERR("Cannot write to msgq (%d)\n", ret);
}
}
static const char *get_l2_desc(uint32_t event)
{
static const char *desc = "<unknown event>";
switch (event) {
case NET_EVENT_IF_DOWN:
desc = "down";
break;
case NET_EVENT_IF_UP:
desc = "up";
break;
}
return desc;
}
static char *get_l3_desc(struct event_msg *msg,
const char **desc, const char **desc2,
char *extra_info, size_t extra_info_len)
{
static const char *desc_unknown = "<unknown event>";
char *info = NULL;
*desc = desc_unknown;
switch (msg->event) {
case NET_EVENT_IPV6_ADDR_ADD:
*desc = "IPv6 address";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_ADDR_DEL:
*desc = "IPv6 address";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_MADDR_ADD:
*desc = "IPv6 mcast address";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_MADDR_DEL:
*desc = "IPv6 mcast address";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_PREFIX_ADD:
*desc = "IPv6 prefix";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_PREFIX_DEL:
*desc = "IPv6 prefix";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_MCAST_JOIN:
*desc = "IPv6 mcast";
*desc2 = "join";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_MCAST_LEAVE:
*desc = "IPv6 mcast";
*desc2 = "leave";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_ROUTER_ADD:
*desc = "IPv6 router";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_ROUTER_DEL:
*desc = "IPv6 router";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_ROUTE_ADD:
*desc = "IPv6 route";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_ROUTE_DEL:
*desc = "IPv6 route";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_DAD_SUCCEED:
*desc = "IPv6 DAD";
*desc2 = "ok";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_DAD_FAILED:
*desc = "IPv6 DAD";
*desc2 = "fail";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_NBR_ADD:
*desc = "IPv6 neighbor";
*desc2 = "add";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV6_NBR_DEL:
*desc = "IPv6 neighbor";
*desc2 = "del";
info = net_addr_ntop(AF_INET6, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV4_ADDR_ADD:
*desc = "IPv4 address";
*desc2 = "add";
info = net_addr_ntop(AF_INET, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV4_ADDR_DEL:
*desc = "IPv4 address";
*desc2 = "del";
info = net_addr_ntop(AF_INET, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV4_ROUTER_ADD:
*desc = "IPv4 router";
*desc2 = "add";
info = net_addr_ntop(AF_INET, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV4_ROUTER_DEL:
*desc = "IPv4 router";
*desc2 = "del";
info = net_addr_ntop(AF_INET, msg->data, extra_info,
extra_info_len);
break;
case NET_EVENT_IPV4_DHCP_START:
*desc = "DHCPv4";
*desc2 = "start";
break;
case NET_EVENT_IPV4_DHCP_BOUND:
*desc = "DHCPv4";
*desc2 = "bound";
#if defined(CONFIG_NET_DHCPV4)
struct net_if_dhcpv4 *data = (struct net_if_dhcpv4 *)msg->data;
info = net_addr_ntop(AF_INET, &data->requested_ip, extra_info,
extra_info_len);
#endif
break;
case NET_EVENT_IPV4_DHCP_STOP:
*desc = "DHCPv4";
*desc2 = "stop";
break;
}
return info;
}
static const char *get_l4_desc(uint32_t event)
{
static const char *desc = "<unknown event>";
switch (event) {
case NET_EVENT_L4_CONNECTED:
desc = "connected";
break;
case NET_EVENT_L4_DISCONNECTED:
desc = "disconnected";
break;
case NET_EVENT_DNS_SERVER_ADD:
desc = "DNS server add";
break;
case NET_EVENT_DNS_SERVER_DEL:
desc = "DNS server del";
break;
case NET_EVENT_COAP_SERVICE_STARTED:
desc = "CoAP service started";
break;
case NET_EVENT_COAP_SERVICE_STOPPED:
desc = "CoAP service stopped";
break;
case NET_EVENT_COAP_OBSERVER_ADDED:
desc = "CoAP observer added";
break;
case NET_EVENT_COAP_OBSERVER_REMOVED:
desc = "CoAP observer removed";
break;
}
return desc;
}
/* We use a separate thread in order not to do any shell printing from
* event handler callback (to avoid stack size issues).
*/
static void event_mon_handler(const struct shell *sh, void *p2, void *p3)
{
char extra_info[NET_IPV6_ADDR_LEN];
struct event_msg msg;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
net_mgmt_init_event_callback(&l2_cb, event_handler,
MONITOR_L2_MASK);
net_mgmt_add_event_callback(&l2_cb);
net_mgmt_init_event_callback(&l3_ipv4_cb, event_handler,
MONITOR_L3_IPV4_MASK);
net_mgmt_add_event_callback(&l3_ipv4_cb);
net_mgmt_init_event_callback(&l3_ipv6_cb, event_handler,
MONITOR_L3_IPV6_MASK);
net_mgmt_add_event_callback(&l3_ipv6_cb);
net_mgmt_init_event_callback(&l4_cb, event_handler,
MONITOR_L4_MASK);
net_mgmt_add_event_callback(&l4_cb);
while (net_event_shutting_down == false) {
const char *layer_str = "<unknown layer>";
const char *desc = "", *desc2 = "";
char *info = NULL;
uint32_t layer;
(void)k_msgq_get(&event_mon_msgq, &msg, K_FOREVER);
if (msg.iface == NULL && msg.event == 0 && msg.len == 0) {
/* This is the stop message */
continue;
}
layer = NET_MGMT_GET_LAYER(msg.event);
if (layer == NET_MGMT_LAYER_L2) {
layer_str = "L2";
desc = get_l2_desc(msg.event);
} else if (layer == NET_MGMT_LAYER_L3) {
layer_str = "L3";
info = get_l3_desc(&msg, &desc, &desc2,
extra_info, NET_IPV6_ADDR_LEN);
} else if (layer == NET_MGMT_LAYER_L4) {
layer_str = "L4";
desc = get_l4_desc(msg.event);
}
PR_INFO("EVENT: %s [%d] %s%s%s%s%s\n", layer_str,
net_if_get_by_iface(msg.iface), desc,
desc2 ? " " : "", desc2 ? desc2 : "",
info ? " " : "", info ? info : "");
}
net_mgmt_del_event_callback(&l2_cb);
net_mgmt_del_event_callback(&l3_ipv4_cb);
net_mgmt_del_event_callback(&l3_ipv6_cb);
net_mgmt_del_event_callback(&l4_cb);
k_msgq_purge(&event_mon_msgq);
net_event_monitoring = false;
net_event_shutting_down = false;
PR_INFO("Network event monitoring %s.\n", "disabled");
}
#endif
static int cmd_net_events_on(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_NET_MGMT_EVENT_MONITOR)
k_tid_t tid;
if (net_event_monitoring) {
PR_INFO("Network event monitoring is already %s.\n",
"enabled");
return -ENOEXEC;
}
tid = k_thread_create(&event_mon, event_mon_stack,
K_THREAD_STACK_SIZEOF(event_mon_stack),
(k_thread_entry_t)event_mon_handler,
(void *)sh, NULL, NULL, THREAD_PRIORITY, 0,
K_FOREVER);
if (!tid) {
PR_ERROR("Cannot create network event monitor thread!");
return -ENOEXEC;
}
k_thread_name_set(tid, "event_mon");
PR_INFO("Network event monitoring %s.\n", "enabled");
net_event_monitoring = true;
net_event_shutting_down = false;
k_thread_start(tid);
#else
PR_INFO("Network management events are not supported. "
"Set CONFIG_NET_MGMT_EVENT_MONITOR to enable it.\n");
#endif
return 0;
}
static int cmd_net_events_off(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_NET_MGMT_EVENT_MONITOR)
static const struct event_msg msg;
int ret;
if (!net_event_monitoring) {
PR_INFO("Network event monitoring is already %s.\n",
"disabled");
return -ENOEXEC;
}
net_event_shutting_down = true;
ret = k_msgq_put(&event_mon_msgq, (void *)&msg, K_MSEC(100));
if (ret < 0) {
PR_ERROR("Cannot write to msgq (%d)\n", ret);
return -ENOEXEC;
}
#else
PR_INFO("Network management events are not supported. "
"Set CONFIG_NET_MGMT_EVENT_MONITOR to enable it.\n");
#endif
return 0;
}
static int cmd_net_events(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_NET_MGMT_EVENT_MONITOR)
PR("Network event monitoring is %s.\n",
net_event_monitoring ? "enabled" : "disabled");
if (!argv[1]) {
PR_INFO("Give 'on' to enable event monitoring and "
"'off' to disable it.\n");
}
#else
PR_INFO("Network management events are not supported. "
"Set CONFIG_NET_MGMT_EVENT_MONITOR to enable it.\n");
#endif
return 0;
}
void events_enable(void)
{
static const char * const argv[] = {
"on",
NULL
};
(void)cmd_net_events_on(shell_backend_uart_get_ptr(), 1, (char **)argv);
}
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_events,
SHELL_CMD(on, NULL, "Turn on network event monitoring.",
cmd_net_events_on),
SHELL_CMD(off, NULL, "Turn off network event monitoring.",
cmd_net_events_off),
SHELL_SUBCMD_SET_END
);
SHELL_SUBCMD_ADD((net), events, &net_cmd_events, "Monitor network management events.",
cmd_net_events, 1, 1);