net: shell: Add network mgmt events monitor support

Add "net events [on|off]" command that can be used to monitor
the generated network management events.

The monitor output looks like this when enabled:

EVENT: L2 [1] up
EVENT: L3 [1] IPv6 mcast address add ff02::1:ff00:1
EVENT: L3 [1] IPv6 mcast join ff02::1:ff00:1
EVENT: L3 [1] IPv6 address add 2001:db8::1
EVENT: L4 [1] connected
EVENT: L3 [1] IPv6 prefix add 2002:5b9b:41a0::
EVENT: L3 [1] IPv6 address add 2002:5b9b:41a0:0:fec2:3dff:fe11:c147
EVENT: L3 [1] IPv6 neighbor add fe80::9ec7:a6ff:fe5e:4735
EVENT: L3 [1] IPv6 router add fe80::9ec7:a6ff:fe5e:4735
EVENT: L3 [1] IPv6 DAD ok fe80::fec2:3dff:fe11:c147
EVENT: L3 [1] IPv6 DAD ok 2001:db8::1
EVENT: L3 [1] IPv6 DAD ok 2002:5b9b:41a0:0:fec2:3dff:fe11:c147
EVENT: L3 [1] IPv4 address add 192.168.1.69
EVENT: L3 [1] DHCPv4 bound 192.168.1.69

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2020-10-02 18:35:55 +03:00 committed by Jukka Rissanen
parent 619dcef498
commit accaab2112
4 changed files with 438 additions and 0 deletions

View file

@ -22,6 +22,8 @@ The following net-shell commands are implemented:
"net conn", "Print information about network connections."
"net dns", "Show how DNS is configured. The command can also be used to
resolve a DNS name. Only available if :option:`CONFIG_DNS_RESOLVER` is set."
"net events", "Enable network event monitoring. Only available if
:option:`CONFIG_NET_MGMT_EVENT_MONITOR` is set."
"net gptp", "Print information about gPTP support. Only available if
:option:`CONFIG_NET_GPTP` is set."
"net iface", "Print information about network interfaces."

View file

@ -36,6 +36,7 @@ config NET_MGMT_EVENT_THREAD_PRIO
config NET_MGMT_EVENT_QUEUE_SIZE
int "Size of event queue"
default 16 if NET_MGMT_EVENT_MONITOR
default 2
range 1 1024
help
@ -51,6 +52,24 @@ config NET_MGMT_EVENT_INFO
and listeners will then be able to get it. Such information depends
on the type of event.
config NET_MGMT_EVENT_MONITOR
bool "Monitor network events from net shell"
depends on NET_SHELL && NET_MGMT_EVENT_INFO
help
Allow user to monitor network events from net shell using
"net events [on|off]" command. The monitoring is disabled by
default. Note that you should probably increase the value of
NET_MGMT_EVENT_QUEUE_SIZE from the default in order not to miss
any events.
config NET_MGMT_EVENT_MONITOR_AUTO_START
bool "Start the event monitor automatically at boot"
depends on NET_MGMT_EVENT_MONITOR && SHELL_BACKEND_SERIAL
help
Allow user to start monitoring network events automatically
when the system starts. The auto start is disabled by default.
The default UART based shell is used to print data.
module = NET_MGMT_EVENT
module-dep = NET_LOG
module-str = Log level for network management event core

View file

@ -19,6 +19,7 @@ LOG_MODULE_REGISTER(net_shell, LOG_LEVEL_DBG);
#include <stdlib.h>
#include <stdio.h>
#include <shell/shell.h>
#include <shell/shell_uart.h>
#include <net/net_if.h>
#include <net/dns_resolve.h>
@ -1887,6 +1888,401 @@ static int cmd_net_dns(const struct shell *shell, size_t argc, char *argv[])
return 0;
}
#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;
}
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 *shell)
{
char extra_info[NET_IPV6_ADDR_LEN];
struct event_msg msg;
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 *shell, 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 *)shell, 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 *shell, 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 *shell, 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;
}
#if defined(CONFIG_NET_GPTP)
static const char *selected_role_str(int port);
@ -4481,6 +4877,14 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dns,
SHELL_SUBCMD_SET_END
);
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_STATIC_SUBCMD_SET_CREATE(net_cmd_gptp,
SHELL_CMD(port, NULL,
"'net gptp [<port>]' prints detailed information about "
@ -4768,6 +5172,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(net_commands,
cmd_net_conn),
SHELL_CMD(dns, &net_cmd_dns, "Show how DNS is configured.",
cmd_net_dns),
SHELL_CMD(events, &net_cmd_events, "Monitor network management events.",
cmd_net_events),
SHELL_CMD(gptp, &net_cmd_gptp, "Print information about gPTP support.",
cmd_net_gptp),
SHELL_CMD(iface, &net_cmd_iface,
@ -4805,5 +5211,14 @@ SHELL_CMD_REGISTER(net, &net_commands, "Networking commands", NULL);
int net_shell_init(void)
{
#if defined(CONFIG_NET_MGMT_EVENT_MONITOR_AUTO_START)
char *argv[] = {
"on",
NULL
};
(void)cmd_net_events_on(shell_backend_uart_get_ptr(), 1, argv);
#endif
return 0;
}

View file

@ -212,6 +212,8 @@ CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=2
CONFIG_NET_MGMT_EVENT_LOG_LEVEL_DBG=y
CONFIG_NET_DEBUG_MGMT_EVENT_STACK=y
CONFIG_NET_MGMT_EVENT_INFO=y
CONFIG_NET_MGMT_EVENT_MONITOR=y
CONFIG_NET_MGMT_EVENT_MONITOR_AUTO_START=y
# IPv4
CONFIG_NET_IPV4=y