In the existing the value received from the other side by the TCP options is used as MSS for transmission. Since the MSS options are an announcement rather then a negotionation, it is likely the receiver will have a different and possibly bigger MSS than allowed by our side. This allow potentially for different a MSS in the receive and transmit path. Directly using the received MSS could cause problems when our MSS is only allowed to be small. At transmission, for that reason take the minimum of the received MSS and our desired MSS to find a value compatible to both sides of the link. Rename the function to net_tcp_get_recv_mss to net_tcp_get_supported_mss to better reflect its function in the new situation. Signed-off-by: Sjors Hettinga <s.a.hettinga@gmail.com>
6066 lines
150 KiB
C
6066 lines
150 KiB
C
/** @file
|
|
* @brief Network shell module
|
|
*
|
|
* Provide some networking shell commands that can be useful to applications.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_shell, LOG_LEVEL_DBG);
|
|
|
|
#include <zephyr/zephyr.h>
|
|
#include <kernel_internal.h>
|
|
#include <zephyr/pm/device.h>
|
|
#include <zephyr/random/rand32.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <zephyr/shell/shell.h>
|
|
#include <zephyr/shell/shell_uart.h>
|
|
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/dns_resolve.h>
|
|
#include <zephyr/net/ppp.h>
|
|
#include <zephyr/net/net_stats.h>
|
|
#include <zephyr/sys/printk.h>
|
|
|
|
#include "route.h"
|
|
#include "icmpv6.h"
|
|
#include "icmpv4.h"
|
|
#include "connection.h"
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
#include "tcp_internal.h"
|
|
#include <zephyr/sys/slist.h>
|
|
#endif
|
|
|
|
#include "ipv6.h"
|
|
|
|
#if defined(CONFIG_NET_ARP)
|
|
#include "ethernet/arp.h"
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_ETHERNET)
|
|
#include <zephyr/net/ethernet.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_ETHERNET_MGMT)
|
|
#include <zephyr/net/ethernet_mgmt.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_VIRTUAL)
|
|
#include <zephyr/net/virtual.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_VIRTUAL_MGMT)
|
|
#include <zephyr/net/virtual_mgmt.h>
|
|
#endif
|
|
|
|
#include <zephyr/net/capture.h>
|
|
|
|
#if defined(CONFIG_NET_GPTP)
|
|
#include <zephyr/net/gptp.h>
|
|
#include "ethernet/gptp/gptp_messages.h"
|
|
#include "ethernet/gptp/gptp_md.h"
|
|
#include "ethernet/gptp/gptp_state.h"
|
|
#include "ethernet/gptp/gptp_data_set.h"
|
|
#include "ethernet/gptp/gptp_private.h"
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_PPP)
|
|
#include <zephyr/net/ppp.h>
|
|
#include "ppp/ppp_internal.h"
|
|
#endif
|
|
|
|
#include "net_shell.h"
|
|
#include "net_stats.h"
|
|
|
|
#include <zephyr/sys/fdtable.h>
|
|
#include "websocket/websocket_internal.h"
|
|
|
|
#define PR(fmt, ...) \
|
|
shell_fprintf(shell, SHELL_NORMAL, fmt, ##__VA_ARGS__)
|
|
|
|
#define PR_SHELL(shell, fmt, ...) \
|
|
shell_fprintf(shell, SHELL_NORMAL, fmt, ##__VA_ARGS__)
|
|
|
|
#define PR_ERROR(fmt, ...) \
|
|
shell_fprintf(shell, SHELL_ERROR, fmt, ##__VA_ARGS__)
|
|
|
|
#define PR_INFO(fmt, ...) \
|
|
shell_fprintf(shell, SHELL_INFO, fmt, ##__VA_ARGS__)
|
|
|
|
#define PR_WARNING(fmt, ...) \
|
|
shell_fprintf(shell, SHELL_WARNING, fmt, ##__VA_ARGS__)
|
|
|
|
#include "net_private.h"
|
|
|
|
struct net_shell_user_data {
|
|
const struct shell *shell;
|
|
void *user_data;
|
|
};
|
|
|
|
static inline const char *addrtype2str(enum net_addr_type addr_type)
|
|
{
|
|
switch (addr_type) {
|
|
case NET_ADDR_ANY:
|
|
return "<unknown type>";
|
|
case NET_ADDR_AUTOCONF:
|
|
return "autoconf";
|
|
case NET_ADDR_DHCP:
|
|
return "DHCP";
|
|
case NET_ADDR_MANUAL:
|
|
return "manual";
|
|
case NET_ADDR_OVERRIDABLE:
|
|
return "overridable";
|
|
}
|
|
|
|
return "<invalid type>";
|
|
}
|
|
|
|
static inline const char *addrstate2str(enum net_addr_state addr_state)
|
|
{
|
|
switch (addr_state) {
|
|
case NET_ADDR_ANY_STATE:
|
|
return "<unknown state>";
|
|
case NET_ADDR_TENTATIVE:
|
|
return "tentative";
|
|
case NET_ADDR_PREFERRED:
|
|
return "preferred";
|
|
case NET_ADDR_DEPRECATED:
|
|
return "deprecated";
|
|
}
|
|
|
|
return "<invalid state>";
|
|
}
|
|
|
|
static const char *iface2str(struct net_if *iface, const char **extra)
|
|
{
|
|
#ifdef CONFIG_NET_L2_IEEE802154
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(IEEE802154)) {
|
|
if (extra) {
|
|
*extra = "=============";
|
|
}
|
|
|
|
return "IEEE 802.15.4";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_ETHERNET
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
|
if (extra) {
|
|
*extra = "========";
|
|
}
|
|
|
|
return "Ethernet";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_VIRTUAL
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
|
|
if (extra) {
|
|
*extra = "=======";
|
|
}
|
|
|
|
return "Virtual";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_PPP
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(PPP)) {
|
|
if (extra) {
|
|
*extra = "===";
|
|
}
|
|
|
|
return "PPP";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_DUMMY
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) {
|
|
if (extra) {
|
|
*extra = "=====";
|
|
}
|
|
|
|
return "Dummy";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_OPENTHREAD
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(OPENTHREAD)) {
|
|
if (extra) {
|
|
*extra = "==========";
|
|
}
|
|
|
|
return "OpenThread";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_BT
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(BLUETOOTH)) {
|
|
if (extra) {
|
|
*extra = "=========";
|
|
}
|
|
|
|
return "Bluetooth";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_OFFLOAD
|
|
if (net_if_is_ip_offloaded(iface)) {
|
|
if (extra) {
|
|
*extra = "==========";
|
|
}
|
|
|
|
return "IP Offload";
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_CANBUS_RAW
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(CANBUS_RAW)) {
|
|
if (extra) {
|
|
*extra = "==========";
|
|
}
|
|
|
|
return "CANBUS_RAW";
|
|
}
|
|
#endif
|
|
|
|
if (extra) {
|
|
*extra = "==============";
|
|
}
|
|
|
|
return "<unknown type>";
|
|
}
|
|
|
|
#if defined(CONFIG_NET_L2_ETHERNET) && defined(CONFIG_NET_NATIVE)
|
|
struct ethernet_capabilities {
|
|
enum ethernet_hw_caps capability;
|
|
const char * const description;
|
|
};
|
|
|
|
#define EC(cap, desc) { .capability = cap, .description = desc }
|
|
|
|
static struct ethernet_capabilities eth_hw_caps[] = {
|
|
EC(ETHERNET_HW_TX_CHKSUM_OFFLOAD, "TX checksum offload"),
|
|
EC(ETHERNET_HW_RX_CHKSUM_OFFLOAD, "RX checksum offload"),
|
|
EC(ETHERNET_HW_VLAN, "Virtual LAN"),
|
|
EC(ETHERNET_HW_VLAN_TAG_STRIP, "VLAN Tag stripping"),
|
|
EC(ETHERNET_AUTO_NEGOTIATION_SET, "Auto negotiation"),
|
|
EC(ETHERNET_LINK_10BASE_T, "10 Mbits"),
|
|
EC(ETHERNET_LINK_100BASE_T, "100 Mbits"),
|
|
EC(ETHERNET_LINK_1000BASE_T, "1 Gbits"),
|
|
EC(ETHERNET_DUPLEX_SET, "Half/full duplex"),
|
|
EC(ETHERNET_PTP, "IEEE 802.1AS gPTP clock"),
|
|
EC(ETHERNET_QAV, "IEEE 802.1Qav (credit shaping)"),
|
|
EC(ETHERNET_QBV, "IEEE 802.1Qbv (scheduled traffic)"),
|
|
EC(ETHERNET_QBU, "IEEE 802.1Qbu (frame preemption)"),
|
|
EC(ETHERNET_TXTIME, "TXTIME"),
|
|
EC(ETHERNET_PROMISC_MODE, "Promiscuous mode"),
|
|
EC(ETHERNET_PRIORITY_QUEUES, "Priority queues"),
|
|
EC(ETHERNET_HW_FILTERING, "MAC address filtering"),
|
|
EC(ETHERNET_DSA_SLAVE_PORT, "DSA slave port"),
|
|
EC(ETHERNET_DSA_MASTER_PORT, "DSA master port"),
|
|
};
|
|
|
|
static void print_supported_ethernet_capabilities(
|
|
const struct shell *shell, struct net_if *iface)
|
|
{
|
|
enum ethernet_hw_caps caps = net_eth_get_hw_capabilities(iface);
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(eth_hw_caps); i++) {
|
|
if (caps & eth_hw_caps[i].capability) {
|
|
PR("\t%s\n", eth_hw_caps[i].description);
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_L2_ETHERNET */
|
|
|
|
#if defined(CONFIG_NET_NATIVE)
|
|
static const char *iface_flags2str(struct net_if *iface)
|
|
{
|
|
static char str[sizeof("POINTOPOINT") + sizeof("PROMISC") +
|
|
sizeof("NO_AUTO_START") + sizeof("SUSPENDED") +
|
|
sizeof("MCAST_FORWARD") + sizeof("IPv4") +
|
|
sizeof("IPv6")];
|
|
int pos = 0;
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_POINTOPOINT)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"POINTOPOINT,");
|
|
}
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_PROMISC)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"PROMISC,");
|
|
}
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_NO_AUTO_START)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"NO_AUTO_START,");
|
|
} else {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"AUTO_START,");
|
|
}
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_FORWARD_MULTICASTS)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"MCAST_FORWARD,");
|
|
}
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_IPV4)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"IPv4,");
|
|
}
|
|
|
|
if (net_if_flag_is_set(iface, NET_IF_IPV6)) {
|
|
pos += snprintk(str + pos, sizeof(str) - pos,
|
|
"IPv6,");
|
|
}
|
|
|
|
/* get rid of last ',' character */
|
|
str[pos - 1] = '\0';
|
|
|
|
return str;
|
|
}
|
|
#endif
|
|
|
|
static void iface_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_NATIVE)
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
struct net_if_ipv6_prefix *prefix;
|
|
struct net_if_router *router;
|
|
struct net_if_ipv6 *ipv6;
|
|
#endif
|
|
#if defined(CONFIG_NET_IPV4)
|
|
struct net_if_ipv4 *ipv4;
|
|
#endif
|
|
#if defined(CONFIG_NET_VLAN)
|
|
struct ethernet_context *eth_ctx;
|
|
#endif
|
|
#if defined(CONFIG_NET_IPV4) || defined(CONFIG_NET_IPV6)
|
|
struct net_if_addr *unicast;
|
|
struct net_if_mcast_addr *mcast;
|
|
#endif
|
|
#if defined(CONFIG_NET_L2_ETHERNET_MGMT)
|
|
struct ethernet_req_params params;
|
|
int ret;
|
|
#endif
|
|
const char *extra;
|
|
#if defined(CONFIG_NET_IPV4) || defined(CONFIG_NET_IPV6)
|
|
int i, count;
|
|
#endif
|
|
|
|
if (data->user_data && data->user_data != iface) {
|
|
return;
|
|
}
|
|
|
|
PR("\nInterface %p (%s) [%d]\n", iface, iface2str(iface, &extra),
|
|
net_if_get_by_iface(iface));
|
|
PR("===========================%s\n", extra);
|
|
|
|
if (!net_if_is_up(iface)) {
|
|
PR_INFO("Interface is down.\n");
|
|
|
|
/* Show detailed information only when user asks information
|
|
* about one specific network interface.
|
|
*/
|
|
if (data->user_data == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_NET_POWER_MANAGEMENT
|
|
if (net_if_is_suspended(iface)) {
|
|
PR_INFO("Interface is suspended, thus not able to tx/rx.\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_VIRTUAL)
|
|
if (!sys_slist_is_empty(&iface->config.virtual_interfaces)) {
|
|
struct virtual_interface_context *ctx, *tmp;
|
|
|
|
PR("Virtual interfaces attached to this : ");
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
|
|
&iface->config.virtual_interfaces,
|
|
ctx, tmp, node) {
|
|
if (ctx->virtual_iface == iface) {
|
|
continue;
|
|
}
|
|
|
|
PR("%d ", net_if_get_by_iface(ctx->virtual_iface));
|
|
}
|
|
|
|
PR("\n");
|
|
}
|
|
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
|
|
struct net_if *orig_iface;
|
|
char *name, buf[CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN];
|
|
|
|
name = net_virtual_get_name(iface, buf, sizeof(buf));
|
|
if (!(name && name[0])) {
|
|
name = "<unknown>";
|
|
}
|
|
|
|
PR("Name : %s\n", name);
|
|
|
|
orig_iface = net_virtual_get_iface(iface);
|
|
if (orig_iface == NULL) {
|
|
PR("No attached network interface.\n");
|
|
} else {
|
|
PR("Attached : %d (%s / %p)\n",
|
|
net_if_get_by_iface(orig_iface),
|
|
iface2str(orig_iface, NULL),
|
|
orig_iface);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_L2_VIRTUAL */
|
|
|
|
if (net_if_get_link_addr(iface) &&
|
|
net_if_get_link_addr(iface)->addr) {
|
|
PR("Link addr : %s\n",
|
|
net_sprint_ll_addr(net_if_get_link_addr(iface)->addr,
|
|
net_if_get_link_addr(iface)->len));
|
|
}
|
|
|
|
PR("MTU : %d\n", net_if_get_mtu(iface));
|
|
PR("Flags : %s\n", iface_flags2str(iface));
|
|
|
|
#if defined(CONFIG_NET_L2_ETHERNET_MGMT)
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
|
count = 0;
|
|
ret = net_mgmt(NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM,
|
|
iface, ¶ms,
|
|
sizeof(struct ethernet_req_params));
|
|
|
|
if (!ret && params.priority_queues_num) {
|
|
count = params.priority_queues_num;
|
|
PR("Priority queues:\n");
|
|
for (i = 0; i < count; ++i) {
|
|
params.qav_param.queue_id = i;
|
|
params.qav_param.type =
|
|
ETHERNET_QAV_PARAM_TYPE_STATUS;
|
|
ret = net_mgmt(
|
|
NET_REQUEST_ETHERNET_GET_QAV_PARAM,
|
|
iface, ¶ms,
|
|
sizeof(struct ethernet_req_params));
|
|
|
|
PR("\t%d: Qav ", i);
|
|
if (ret) {
|
|
PR("not supported\n");
|
|
} else {
|
|
PR("%s\n",
|
|
params.qav_param.enabled ?
|
|
"enabled" :
|
|
"disabled");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
|
|
PR("Promiscuous mode : %s\n",
|
|
net_if_is_promisc(iface) ? "enabled" : "disabled");
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
|
eth_ctx = net_if_l2_data(iface);
|
|
|
|
if (eth_ctx->vlan_enabled) {
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
if (eth_ctx->vlan[i].iface != iface ||
|
|
eth_ctx->vlan[i].tag ==
|
|
NET_VLAN_TAG_UNSPEC) {
|
|
continue;
|
|
}
|
|
|
|
PR("VLAN tag : %d (0x%x)\n",
|
|
eth_ctx->vlan[i].tag,
|
|
eth_ctx->vlan[i].tag);
|
|
}
|
|
} else {
|
|
PR("VLAN not enabled\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_L2_ETHERNET
|
|
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
|
PR("Ethernet capabilities supported:\n");
|
|
print_supported_ethernet_capabilities(shell, iface);
|
|
}
|
|
#endif /* CONFIG_NET_L2_ETHERNET */
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
count = 0;
|
|
|
|
if (!net_if_flag_is_set(iface, NET_IF_IPV6)) {
|
|
PR("%s not %s for this interface.\n", "IPv6", "enabled");
|
|
ipv6 = NULL;
|
|
goto skip_ipv6;
|
|
}
|
|
|
|
ipv6 = iface->config.ip.ipv6;
|
|
|
|
PR("IPv6 unicast addresses (max %d):\n", NET_IF_MAX_IPV6_ADDR);
|
|
for (i = 0; ipv6 && i < NET_IF_MAX_IPV6_ADDR; i++) {
|
|
unicast = &ipv6->unicast[i];
|
|
|
|
if (!unicast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
PR("\t%s %s %s%s%s\n",
|
|
net_sprint_ipv6_addr(&unicast->address.in6_addr),
|
|
addrtype2str(unicast->addr_type),
|
|
addrstate2str(unicast->addr_state),
|
|
unicast->is_infinite ? " infinite" : "",
|
|
unicast->is_mesh_local ? " meshlocal" : "");
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
PR("IPv6 multicast addresses (max %d):\n", NET_IF_MAX_IPV6_MADDR);
|
|
for (i = 0; ipv6 && i < NET_IF_MAX_IPV6_MADDR; i++) {
|
|
mcast = &ipv6->mcast[i];
|
|
|
|
if (!mcast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
PR("\t%s\n", net_sprint_ipv6_addr(&mcast->address.in6_addr));
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
PR("IPv6 prefixes (max %d):\n", NET_IF_MAX_IPV6_PREFIX);
|
|
for (i = 0; ipv6 && i < NET_IF_MAX_IPV6_PREFIX; i++) {
|
|
prefix = &ipv6->prefix[i];
|
|
|
|
if (!prefix->is_used) {
|
|
continue;
|
|
}
|
|
|
|
PR("\t%s/%d%s\n",
|
|
net_sprint_ipv6_addr(&prefix->prefix),
|
|
prefix->len, prefix->is_infinite ? " infinite" : "");
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
|
|
router = net_if_ipv6_router_find_default(iface, NULL);
|
|
if (router) {
|
|
PR("IPv6 default router :\n");
|
|
PR("\t%s%s\n",
|
|
net_sprint_ipv6_addr(&router->address.in6_addr),
|
|
router->is_infinite ? " infinite" : "");
|
|
}
|
|
|
|
skip_ipv6:
|
|
|
|
if (ipv6) {
|
|
PR("IPv6 hop limit : %d\n",
|
|
ipv6->hop_limit);
|
|
PR("IPv6 base reachable time : %d\n",
|
|
ipv6->base_reachable_time);
|
|
PR("IPv6 reachable time : %d\n",
|
|
ipv6->reachable_time);
|
|
PR("IPv6 retransmit timer : %d\n",
|
|
ipv6->retrans_timer);
|
|
}
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
/* No need to print IPv4 information for interface that does not
|
|
* support that protocol.
|
|
*/
|
|
if (
|
|
#if defined(CONFIG_NET_L2_IEEE802154)
|
|
(net_if_l2(iface) == &NET_L2_GET_NAME(IEEE802154)) ||
|
|
#endif
|
|
#if defined(CONFIG_NET_L2_BT)
|
|
(net_if_l2(iface) == &NET_L2_GET_NAME(BLUETOOTH)) ||
|
|
#endif
|
|
0) {
|
|
PR_WARNING("%s not %s for this interface.\n", "IPv4",
|
|
"supported");
|
|
return;
|
|
}
|
|
|
|
count = 0;
|
|
|
|
if (!net_if_flag_is_set(iface, NET_IF_IPV4)) {
|
|
PR("%s not %s for this interface.\n", "IPv4", "enabled");
|
|
ipv4 = NULL;
|
|
goto skip_ipv4;
|
|
}
|
|
|
|
ipv4 = iface->config.ip.ipv4;
|
|
|
|
PR("IPv4 unicast addresses (max %d):\n", NET_IF_MAX_IPV4_ADDR);
|
|
for (i = 0; ipv4 && i < NET_IF_MAX_IPV4_ADDR; i++) {
|
|
unicast = &ipv4->unicast[i];
|
|
|
|
if (!unicast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
PR("\t%s %s %s%s\n",
|
|
net_sprint_ipv4_addr(&unicast->address.in_addr),
|
|
addrtype2str(unicast->addr_type),
|
|
addrstate2str(unicast->addr_state),
|
|
unicast->is_infinite ? " infinite" : "");
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
PR("IPv4 multicast addresses (max %d):\n", NET_IF_MAX_IPV4_MADDR);
|
|
for (i = 0; ipv4 && i < NET_IF_MAX_IPV4_MADDR; i++) {
|
|
mcast = &ipv4->mcast[i];
|
|
|
|
if (!mcast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
PR("\t%s\n", net_sprint_ipv4_addr(&mcast->address.in_addr));
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
|
|
skip_ipv4:
|
|
|
|
if (ipv4) {
|
|
PR("IPv4 gateway : %s\n",
|
|
net_sprint_ipv4_addr(&ipv4->gw));
|
|
PR("IPv4 netmask : %s\n",
|
|
net_sprint_ipv4_addr(&ipv4->netmask));
|
|
}
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
PR("DHCPv4 lease time : %u\n",
|
|
iface->config.dhcpv4.lease_time);
|
|
PR("DHCPv4 renew time : %u\n",
|
|
iface->config.dhcpv4.renewal_time);
|
|
PR("DHCPv4 server : %s\n",
|
|
net_sprint_ipv4_addr(&iface->config.dhcpv4.server_id));
|
|
PR("DHCPv4 requested : %s\n",
|
|
net_sprint_ipv4_addr(&iface->config.dhcpv4.requested_ip));
|
|
PR("DHCPv4 state : %s\n",
|
|
net_dhcpv4_state_name(iface->config.dhcpv4.state));
|
|
PR("DHCPv4 attempts : %d\n",
|
|
iface->config.dhcpv4.attempts);
|
|
#endif /* CONFIG_NET_DHCPV4 */
|
|
|
|
#else
|
|
ARG_UNUSED(iface);
|
|
ARG_UNUSED(user_data);
|
|
|
|
#endif /* CONFIG_NET_NATIVE */
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ROUTE) && defined(CONFIG_NET_NATIVE)
|
|
static void route_cb(struct net_route_entry *entry, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct net_if *iface = data->user_data;
|
|
struct net_route_nexthop *nexthop_route;
|
|
int count;
|
|
uint32_t now = k_uptime_get_32();
|
|
|
|
if (entry->iface != iface) {
|
|
return;
|
|
}
|
|
|
|
PR("IPv6 prefix : %s/%d\n", net_sprint_ipv6_addr(&entry->addr),
|
|
entry->prefix_len);
|
|
|
|
count = 0;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&entry->nexthop, nexthop_route, node) {
|
|
struct net_linkaddr_storage *lladdr;
|
|
char remaining_str[sizeof("01234567890 sec")];
|
|
uint32_t remaining;
|
|
|
|
if (!nexthop_route->nbr) {
|
|
continue;
|
|
}
|
|
|
|
PR("\tneighbor : %p\t", nexthop_route->nbr);
|
|
|
|
if (nexthop_route->nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
PR("addr : <unknown>\t");
|
|
} else {
|
|
lladdr = net_nbr_get_lladdr(nexthop_route->nbr->idx);
|
|
|
|
PR("addr : %s\t", net_sprint_ll_addr(lladdr->addr,
|
|
lladdr->len));
|
|
}
|
|
|
|
if (entry->is_infinite) {
|
|
snprintk(remaining_str, sizeof(remaining_str) - 1,
|
|
"infinite");
|
|
} else {
|
|
remaining = net_timeout_remaining(&entry->lifetime, now);
|
|
snprintk(remaining_str, sizeof(remaining_str) - 1,
|
|
"%u sec", remaining);
|
|
}
|
|
|
|
PR("lifetime : %s\n", remaining_str);
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
PR("\t<none>\n");
|
|
}
|
|
}
|
|
|
|
static void iface_per_route_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
const char *extra;
|
|
|
|
PR("\nIPv6 routes for interface %d (%p) (%s)\n",
|
|
net_if_get_by_iface(iface), iface,
|
|
iface2str(iface, &extra));
|
|
PR("=========================================%s\n", extra);
|
|
|
|
data->user_data = iface;
|
|
|
|
net_route_foreach(route_cb, data);
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE */
|
|
|
|
#if defined(CONFIG_NET_ROUTE_MCAST) && defined(CONFIG_NET_NATIVE)
|
|
static void route_mcast_cb(struct net_route_entry_mcast *entry,
|
|
void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct net_if *iface = data->user_data;
|
|
const char *extra;
|
|
|
|
if (entry->iface != iface) {
|
|
return;
|
|
}
|
|
|
|
PR("IPv6 multicast route %p for interface %d (%p) (%s)\n", entry,
|
|
net_if_get_by_iface(iface), iface, iface2str(iface, &extra));
|
|
PR("==========================================================="
|
|
"%s\n", extra);
|
|
|
|
PR("IPv6 group : %s\n", net_sprint_ipv6_addr(&entry->group));
|
|
PR("IPv6 group len : %d\n", entry->prefix_len);
|
|
PR("Lifetime : %u\n", entry->lifetime);
|
|
}
|
|
|
|
static void iface_per_mcast_route_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
|
|
data->user_data = iface;
|
|
|
|
net_route_mcast_foreach(route_mcast_cb, NULL, data);
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE_MCAST */
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
|
|
#if NET_TC_COUNT > 1
|
|
static const char *priority2str(enum net_priority priority)
|
|
{
|
|
switch (priority) {
|
|
case NET_PRIORITY_BK:
|
|
return "BK"; /* Background */
|
|
case NET_PRIORITY_BE:
|
|
return "BE"; /* Best effort */
|
|
case NET_PRIORITY_EE:
|
|
return "EE"; /* Excellent effort */
|
|
case NET_PRIORITY_CA:
|
|
return "CA"; /* Critical applications */
|
|
case NET_PRIORITY_VI:
|
|
return "VI"; /* Video, < 100 ms latency and jitter */
|
|
case NET_PRIORITY_VO:
|
|
return "VO"; /* Voice, < 10 ms latency and jitter */
|
|
case NET_PRIORITY_IC:
|
|
return "IC"; /* Internetwork control */
|
|
case NET_PRIORITY_NC:
|
|
return "NC"; /* Network control */
|
|
}
|
|
|
|
return "??";
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_ETHERNET) && \
|
|
defined(CONFIG_NET_STATISTICS_USER_API)
|
|
static void print_eth_stats(struct net_if *iface, struct net_stats_eth *data,
|
|
const struct shell *shell)
|
|
{
|
|
PR("Statistics for Ethernet interface %p [%d]\n", iface,
|
|
net_if_get_by_iface(iface));
|
|
|
|
PR("Bytes received : %u\n", data->bytes.received);
|
|
PR("Bytes sent : %u\n", data->bytes.sent);
|
|
PR("Packets received : %u\n", data->pkts.rx);
|
|
PR("Packets sent : %u\n", data->pkts.tx);
|
|
PR("Bcast received : %u\n", data->broadcast.rx);
|
|
PR("Bcast sent : %u\n", data->broadcast.tx);
|
|
PR("Mcast received : %u\n", data->multicast.rx);
|
|
PR("Mcast sent : %u\n", data->multicast.tx);
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_ETHERNET_VENDOR)
|
|
if (data->vendor) {
|
|
PR("Vendor specific statistics for Ethernet "
|
|
"interface %p [%d]:\n",
|
|
iface, net_if_get_by_iface(iface));
|
|
size_t i = 0;
|
|
|
|
do {
|
|
PR("%s : %u\n", data->vendor[i].key,
|
|
data->vendor[i].value);
|
|
i++;
|
|
} while (data->vendor[i].key);
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_PPP) && \
|
|
defined(CONFIG_NET_STATISTICS_USER_API)
|
|
static void print_ppp_stats(struct net_if *iface, struct net_stats_ppp *data,
|
|
const struct shell *shell)
|
|
{
|
|
PR("Frames recv %u\n", data->pkts.rx);
|
|
PR("Frames sent %u\n", data->pkts.tx);
|
|
PR("Frames dropped %u\n", data->drop);
|
|
PR("Bad FCS %u\n", data->chkerr);
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS_PPP && CONFIG_NET_STATISTICS_USER_API */
|
|
|
|
#if !defined(CONFIG_NET_NATIVE)
|
|
#define GET_STAT(a, b) 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) || \
|
|
defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
|
|
#if (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1)
|
|
static char *get_net_pkt_tc_stats_detail(struct net_if *iface, int i,
|
|
bool is_tx)
|
|
{
|
|
static char extra_stats[sizeof("\t[0=xxxx us]") +
|
|
sizeof("->xxxx") *
|
|
NET_PKT_DETAIL_STATS_COUNT];
|
|
int j, total = 0, pos = 0;
|
|
|
|
pos += snprintk(extra_stats, sizeof(extra_stats), "\t[0");
|
|
|
|
for (j = 0; j < NET_PKT_DETAIL_STATS_COUNT; j++) {
|
|
net_stats_t count = 0;
|
|
uint32_t avg;
|
|
|
|
if (is_tx) {
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) && (NET_TC_TX_COUNT > 1)
|
|
count = GET_STAT(iface,
|
|
tc.sent[i].tx_time_detail[j].count);
|
|
#endif
|
|
} else {
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL) && (NET_TC_RX_COUNT > 1)
|
|
count = GET_STAT(iface,
|
|
tc.recv[i].rx_time_detail[j].count);
|
|
#endif
|
|
}
|
|
|
|
if (count == 0) {
|
|
break;
|
|
}
|
|
|
|
if (is_tx) {
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL) && (NET_TC_TX_COUNT > 1)
|
|
avg = (uint32_t)(GET_STAT(iface,
|
|
tc.sent[i].tx_time_detail[j].sum) /
|
|
(uint64_t)count);
|
|
#endif
|
|
} else {
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL) && (NET_TC_RX_COUNT > 1)
|
|
avg = (uint32_t)(GET_STAT(iface,
|
|
tc.recv[i].rx_time_detail[j].sum) /
|
|
(uint64_t)count);
|
|
#endif
|
|
}
|
|
|
|
if (avg == 0) {
|
|
continue;
|
|
}
|
|
|
|
total += avg;
|
|
|
|
pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
|
|
"->%u", avg);
|
|
}
|
|
|
|
if (total == 0U) {
|
|
return "\0";
|
|
}
|
|
|
|
pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
|
|
"=%u us]", total);
|
|
|
|
return extra_stats;
|
|
}
|
|
#endif /* (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1) */
|
|
|
|
#if (NET_TC_TX_COUNT <= 1) || (NET_TC_RX_COUNT <= 1)
|
|
static char *get_net_pkt_stats_detail(struct net_if *iface, bool is_tx)
|
|
{
|
|
static char extra_stats[sizeof("\t[0=xxxx us]") + sizeof("->xxxx") *
|
|
NET_PKT_DETAIL_STATS_COUNT];
|
|
int j, total = 0, pos = 0;
|
|
|
|
pos += snprintk(extra_stats, sizeof(extra_stats), "\t[0");
|
|
|
|
for (j = 0; j < NET_PKT_DETAIL_STATS_COUNT; j++) {
|
|
net_stats_t count;
|
|
uint32_t avg;
|
|
|
|
if (is_tx) {
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL)
|
|
count = GET_STAT(iface, tx_time_detail[j].count);
|
|
#endif
|
|
} else {
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
|
|
count = GET_STAT(iface, rx_time_detail[j].count);
|
|
#endif
|
|
}
|
|
|
|
if (count == 0) {
|
|
break;
|
|
}
|
|
|
|
if (is_tx) {
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS_DETAIL)
|
|
avg = (uint32_t)(GET_STAT(iface,
|
|
tx_time_detail[j].sum) /
|
|
(uint64_t)count);
|
|
#endif
|
|
} else {
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS_DETAIL)
|
|
avg = (uint32_t)(GET_STAT(iface,
|
|
rx_time_detail[j].sum) /
|
|
(uint64_t)count);
|
|
#endif
|
|
}
|
|
|
|
if (avg == 0) {
|
|
continue;
|
|
}
|
|
|
|
total += avg;
|
|
|
|
pos += snprintk(extra_stats + pos,
|
|
sizeof(extra_stats) - pos,
|
|
"->%u", avg);
|
|
}
|
|
|
|
if (total == 0U) {
|
|
return "\0";
|
|
}
|
|
|
|
pos += snprintk(extra_stats + pos, sizeof(extra_stats) - pos,
|
|
"=%u us]", total);
|
|
|
|
return extra_stats;
|
|
}
|
|
#endif /* (NET_TC_TX_COUNT == 1) || (NET_TC_RX_COUNT == 1) */
|
|
|
|
#else /* CONFIG_NET_PKT_TXTIME_STATS_DETAIL ||
|
|
CONFIG_NET_PKT_RXTIME_STATS_DETAIL */
|
|
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS) || \
|
|
defined(CONFIG_NET_PKT_RXTIME_STATS)
|
|
|
|
#if (NET_TC_TX_COUNT > 1) || (NET_TC_RX_COUNT > 1)
|
|
static char *get_net_pkt_tc_stats_detail(struct net_if *iface, int i,
|
|
bool is_tx)
|
|
{
|
|
ARG_UNUSED(iface);
|
|
ARG_UNUSED(i);
|
|
ARG_UNUSED(is_tx);
|
|
|
|
return "\0";
|
|
}
|
|
#endif
|
|
|
|
#if (NET_TC_TX_COUNT == 1) || (NET_TC_RX_COUNT == 1)
|
|
static char *get_net_pkt_stats_detail(struct net_if *iface, bool is_tx)
|
|
{
|
|
ARG_UNUSED(iface);
|
|
ARG_UNUSED(is_tx);
|
|
|
|
return "\0";
|
|
}
|
|
#endif
|
|
#endif /* CONFIG_NET_PKT_TXTIME_STATS) || CONFIG_NET_PKT_RXTIME_STATS */
|
|
#endif /* CONFIG_NET_PKT_TXTIME_STATS_DETAIL ||
|
|
CONFIG_NET_PKT_RXTIME_STATS_DETAIL */
|
|
|
|
static void print_tc_tx_stats(const struct shell *shell, struct net_if *iface)
|
|
{
|
|
#if NET_TC_TX_COUNT > 1
|
|
int i;
|
|
|
|
PR("TX traffic class statistics:\n");
|
|
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS)
|
|
PR("TC Priority\tSent pkts\tbytes\ttime\n");
|
|
|
|
for (i = 0; i < NET_TC_TX_COUNT; i++) {
|
|
net_stats_t count = GET_STAT(iface,
|
|
tc.sent[i].tx_time.count);
|
|
if (count == 0) {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\t-\n", i,
|
|
priority2str(GET_STAT(iface, tc.sent[i].priority)),
|
|
GET_STAT(iface, tc.sent[i].priority),
|
|
GET_STAT(iface, tc.sent[i].pkts),
|
|
GET_STAT(iface, tc.sent[i].bytes));
|
|
} else {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\t%u us%s\n", i,
|
|
priority2str(GET_STAT(iface, tc.sent[i].priority)),
|
|
GET_STAT(iface, tc.sent[i].priority),
|
|
GET_STAT(iface, tc.sent[i].pkts),
|
|
GET_STAT(iface, tc.sent[i].bytes),
|
|
(uint32_t)(GET_STAT(iface,
|
|
tc.sent[i].tx_time.sum) /
|
|
(uint64_t)count),
|
|
get_net_pkt_tc_stats_detail(iface, i, true));
|
|
}
|
|
}
|
|
#else
|
|
PR("TC Priority\tSent pkts\tbytes\n");
|
|
|
|
for (i = 0; i < NET_TC_TX_COUNT; i++) {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\n", i,
|
|
priority2str(GET_STAT(iface, tc.sent[i].priority)),
|
|
GET_STAT(iface, tc.sent[i].priority),
|
|
GET_STAT(iface, tc.sent[i].pkts),
|
|
GET_STAT(iface, tc.sent[i].bytes));
|
|
}
|
|
#endif /* CONFIG_NET_PKT_TXTIME_STATS */
|
|
#else
|
|
ARG_UNUSED(shell);
|
|
|
|
#if defined(CONFIG_NET_PKT_TXTIME_STATS)
|
|
net_stats_t count = GET_STAT(iface, tx_time.count);
|
|
|
|
if (count != 0) {
|
|
PR("Avg %s net_pkt (%u) time %lu us%s\n", "TX", count,
|
|
(uint32_t)(GET_STAT(iface, tx_time.sum) / (uint64_t)count),
|
|
get_net_pkt_stats_detail(iface, true));
|
|
}
|
|
#else
|
|
ARG_UNUSED(iface);
|
|
#endif /* CONFIG_NET_PKT_TXTIME_STATS */
|
|
#endif /* NET_TC_TX_COUNT > 1 */
|
|
}
|
|
|
|
static void print_tc_rx_stats(const struct shell *shell, struct net_if *iface)
|
|
{
|
|
#if NET_TC_RX_COUNT > 1
|
|
int i;
|
|
|
|
PR("RX traffic class statistics:\n");
|
|
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS)
|
|
PR("TC Priority\tRecv pkts\tbytes\ttime\n");
|
|
|
|
for (i = 0; i < NET_TC_RX_COUNT; i++) {
|
|
net_stats_t count = GET_STAT(iface,
|
|
tc.recv[i].rx_time.count);
|
|
if (count == 0) {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\t-\n", i,
|
|
priority2str(GET_STAT(iface, tc.recv[i].priority)),
|
|
GET_STAT(iface, tc.recv[i].priority),
|
|
GET_STAT(iface, tc.recv[i].pkts),
|
|
GET_STAT(iface, tc.recv[i].bytes));
|
|
} else {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\t%u us%s\n", i,
|
|
priority2str(GET_STAT(iface, tc.recv[i].priority)),
|
|
GET_STAT(iface, tc.recv[i].priority),
|
|
GET_STAT(iface, tc.recv[i].pkts),
|
|
GET_STAT(iface, tc.recv[i].bytes),
|
|
(uint32_t)(GET_STAT(iface,
|
|
tc.recv[i].rx_time.sum) /
|
|
(uint64_t)count),
|
|
get_net_pkt_tc_stats_detail(iface, i, false));
|
|
}
|
|
}
|
|
#else
|
|
PR("TC Priority\tRecv pkts\tbytes\n");
|
|
|
|
for (i = 0; i < NET_TC_RX_COUNT; i++) {
|
|
PR("[%d] %s (%d)\t%d\t\t%d\n", i,
|
|
priority2str(GET_STAT(iface, tc.recv[i].priority)),
|
|
GET_STAT(iface, tc.recv[i].priority),
|
|
GET_STAT(iface, tc.recv[i].pkts),
|
|
GET_STAT(iface, tc.recv[i].bytes));
|
|
}
|
|
#endif /* CONFIG_NET_PKT_RXTIME_STATS */
|
|
#else
|
|
ARG_UNUSED(shell);
|
|
|
|
#if defined(CONFIG_NET_PKT_RXTIME_STATS)
|
|
net_stats_t count = GET_STAT(iface, rx_time.count);
|
|
|
|
if (count != 0) {
|
|
PR("Avg %s net_pkt (%u) time %lu us%s\n", "RX", count,
|
|
(uint32_t)(GET_STAT(iface, rx_time.sum) / (uint64_t)count),
|
|
get_net_pkt_stats_detail(iface, false));
|
|
}
|
|
#else
|
|
ARG_UNUSED(iface);
|
|
#endif /* CONFIG_NET_PKT_RXTIME_STATS */
|
|
|
|
#endif /* NET_TC_RX_COUNT > 1 */
|
|
}
|
|
|
|
static void print_net_pm_stats(const struct shell *shell, struct net_if *iface)
|
|
{
|
|
#if defined(CONFIG_NET_STATISTICS_POWER_MANAGEMENT)
|
|
PR("PM suspend stats:\n");
|
|
PR("\tLast time : %u ms\n",
|
|
GET_STAT(iface, pm.last_suspend_time));
|
|
PR("\tAverage time : %u ms\n",
|
|
(uint32_t)(GET_STAT(iface, pm.overall_suspend_time) /
|
|
GET_STAT(iface, pm.suspend_count)));
|
|
PR("\tTotal time : %" PRIu64 " ms\n",
|
|
GET_STAT(iface, pm.overall_suspend_time));
|
|
PR("\tHow many times: %u\n",
|
|
GET_STAT(iface, pm.suspend_count));
|
|
#else
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(iface);
|
|
#endif
|
|
}
|
|
|
|
static void net_shell_print_statistics(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
|
|
if (iface) {
|
|
const char *extra;
|
|
|
|
PR("\nInterface %p (%s) [%d]\n", iface,
|
|
iface2str(iface, &extra), net_if_get_by_iface(iface));
|
|
PR("===========================%s\n", extra);
|
|
} else {
|
|
PR("\nGlobal statistics\n");
|
|
PR("=================\n");
|
|
}
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_IPV6) && defined(CONFIG_NET_NATIVE_IPV6)
|
|
PR("IPv6 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n",
|
|
GET_STAT(iface, ipv6.recv),
|
|
GET_STAT(iface, ipv6.sent),
|
|
GET_STAT(iface, ipv6.drop),
|
|
GET_STAT(iface, ipv6.forwarded));
|
|
#if defined(CONFIG_NET_STATISTICS_IPV6_ND)
|
|
PR("IPv6 ND recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, ipv6_nd.recv),
|
|
GET_STAT(iface, ipv6_nd.sent),
|
|
GET_STAT(iface, ipv6_nd.drop));
|
|
#endif /* CONFIG_NET_STATISTICS_IPV6_ND */
|
|
#if defined(CONFIG_NET_STATISTICS_MLD)
|
|
PR("IPv6 MLD recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, ipv6_mld.recv),
|
|
GET_STAT(iface, ipv6_mld.sent),
|
|
GET_STAT(iface, ipv6_mld.drop));
|
|
#endif /* CONFIG_NET_STATISTICS_MLD */
|
|
#endif /* CONFIG_NET_STATISTICS_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_IPV4) && defined(CONFIG_NET_NATIVE_IPV4)
|
|
PR("IPv4 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n",
|
|
GET_STAT(iface, ipv4.recv),
|
|
GET_STAT(iface, ipv4.sent),
|
|
GET_STAT(iface, ipv4.drop),
|
|
GET_STAT(iface, ipv4.forwarded));
|
|
#endif /* CONFIG_NET_STATISTICS_IPV4 */
|
|
|
|
PR("IP vhlerr %d\thblener\t%d\tlblener\t%d\n",
|
|
GET_STAT(iface, ip_errors.vhlerr),
|
|
GET_STAT(iface, ip_errors.hblenerr),
|
|
GET_STAT(iface, ip_errors.lblenerr));
|
|
PR("IP fragerr %d\tchkerr\t%d\tprotoer\t%d\n",
|
|
GET_STAT(iface, ip_errors.fragerr),
|
|
GET_STAT(iface, ip_errors.chkerr),
|
|
GET_STAT(iface, ip_errors.protoerr));
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_ICMP) && defined(CONFIG_NET_NATIVE_IPV4)
|
|
PR("ICMP recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, icmp.recv),
|
|
GET_STAT(iface, icmp.sent),
|
|
GET_STAT(iface, icmp.drop));
|
|
PR("ICMP typeer %d\tchkerr\t%d\n",
|
|
GET_STAT(iface, icmp.typeerr),
|
|
GET_STAT(iface, icmp.chkerr));
|
|
#endif
|
|
#if defined(CONFIG_NET_STATISTICS_IGMP)
|
|
PR("IGMP recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, ipv4_igmp.recv),
|
|
GET_STAT(iface, ipv4_igmp.sent),
|
|
GET_STAT(iface, ipv4_igmp.drop));
|
|
#endif /* CONFIG_NET_STATISTICS_IGMP */
|
|
#if defined(CONFIG_NET_STATISTICS_UDP) && defined(CONFIG_NET_NATIVE_UDP)
|
|
PR("UDP recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, udp.recv),
|
|
GET_STAT(iface, udp.sent),
|
|
GET_STAT(iface, udp.drop));
|
|
PR("UDP chkerr %d\n",
|
|
GET_STAT(iface, udp.chkerr));
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
PR("TCP bytes recv %u\tsent\t%d\tresent\t%d\n",
|
|
GET_STAT(iface, tcp.bytes.received),
|
|
GET_STAT(iface, tcp.bytes.sent),
|
|
GET_STAT(iface, tcp.resent));
|
|
PR("TCP seg recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(iface, tcp.recv),
|
|
GET_STAT(iface, tcp.sent),
|
|
GET_STAT(iface, tcp.seg_drop));
|
|
PR("TCP seg resent %d\tchkerr\t%d\tackerr\t%d\n",
|
|
GET_STAT(iface, tcp.rexmit),
|
|
GET_STAT(iface, tcp.chkerr),
|
|
GET_STAT(iface, tcp.ackerr));
|
|
PR("TCP seg rsterr %d\trst\t%d\n",
|
|
GET_STAT(iface, tcp.rsterr),
|
|
GET_STAT(iface, tcp.rst));
|
|
PR("TCP conn drop %d\tconnrst\t%d\n",
|
|
GET_STAT(iface, tcp.conndrop),
|
|
GET_STAT(iface, tcp.connrst));
|
|
PR("TCP pkt drop %d\n", GET_STAT(iface, tcp.drop));
|
|
#endif
|
|
|
|
PR("Bytes received %u\n", GET_STAT(iface, bytes.received));
|
|
PR("Bytes sent %u\n", GET_STAT(iface, bytes.sent));
|
|
PR("Processing err %d\n", GET_STAT(iface, processing_error));
|
|
|
|
print_tc_tx_stats(shell, iface);
|
|
print_tc_rx_stats(shell, iface);
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_ETHERNET) && \
|
|
defined(CONFIG_NET_STATISTICS_USER_API)
|
|
if (iface && net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
|
|
struct net_stats_eth eth_data;
|
|
int ret;
|
|
|
|
ret = net_mgmt(NET_REQUEST_STATS_GET_ETHERNET, iface,
|
|
ð_data, sizeof(eth_data));
|
|
if (!ret) {
|
|
print_eth_stats(iface, ð_data, shell);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_PPP) && \
|
|
defined(CONFIG_NET_STATISTICS_USER_API)
|
|
if (iface && net_if_l2(iface) == &NET_L2_GET_NAME(PPP)) {
|
|
struct net_stats_ppp ppp_data;
|
|
int ret;
|
|
|
|
ret = net_mgmt(NET_REQUEST_STATS_GET_PPP, iface,
|
|
&ppp_data, sizeof(ppp_data));
|
|
if (!ret) {
|
|
print_ppp_stats(iface, &ppp_data, shell);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS_PPP && CONFIG_NET_STATISTICS_USER_API */
|
|
|
|
print_net_pm_stats(shell, iface);
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS */
|
|
|
|
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
|
|
static void get_addresses(struct net_context *context,
|
|
char addr_local[], int local_len,
|
|
char addr_remote[], int remote_len)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
if (context->local.family == AF_INET6) {
|
|
snprintk(addr_local, local_len, "[%s]:%u",
|
|
net_sprint_ipv6_addr(
|
|
net_sin6_ptr(&context->local)->sin6_addr),
|
|
ntohs(net_sin6_ptr(&context->local)->sin6_port));
|
|
snprintk(addr_remote, remote_len, "[%s]:%u",
|
|
net_sprint_ipv6_addr(
|
|
&net_sin6(&context->remote)->sin6_addr),
|
|
ntohs(net_sin6(&context->remote)->sin6_port));
|
|
} else
|
|
#endif
|
|
#if defined(CONFIG_NET_IPV4)
|
|
if (context->local.family == AF_INET) {
|
|
snprintk(addr_local, local_len, "%s:%d",
|
|
net_sprint_ipv4_addr(
|
|
net_sin_ptr(&context->local)->sin_addr),
|
|
ntohs(net_sin_ptr(&context->local)->sin_port));
|
|
snprintk(addr_remote, remote_len, "%s:%d",
|
|
net_sprint_ipv4_addr(
|
|
&net_sin(&context->remote)->sin_addr),
|
|
ntohs(net_sin(&context->remote)->sin_port));
|
|
} else
|
|
#endif
|
|
if (context->local.family == AF_UNSPEC) {
|
|
snprintk(addr_local, local_len, "AF_UNSPEC");
|
|
} else if (context->local.family == AF_PACKET) {
|
|
snprintk(addr_local, local_len, "AF_PACKET");
|
|
} else if (context->local.family == AF_CAN) {
|
|
snprintk(addr_local, local_len, "AF_CAN");
|
|
} else {
|
|
snprintk(addr_local, local_len, "AF_UNK(%d)",
|
|
context->local.family);
|
|
}
|
|
}
|
|
|
|
static void context_cb(struct net_context *context, void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
|
|
#define ADDR_LEN NET_IPV4_ADDR_LEN
|
|
#else
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#endif
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
/* +7 for []:port */
|
|
char addr_local[ADDR_LEN + 7];
|
|
char addr_remote[ADDR_LEN + 7] = "";
|
|
|
|
get_addresses(context, addr_local, sizeof(addr_local),
|
|
addr_remote, sizeof(addr_remote));
|
|
|
|
PR("[%2d] %p\t%d %c%c%c %16s\t%16s\n",
|
|
(*count) + 1, context,
|
|
net_if_get_by_iface(net_context_get_iface(context)),
|
|
net_context_get_family(context) == AF_INET6 ? '6' :
|
|
(net_context_get_family(context) == AF_INET ? '4' : ' '),
|
|
net_context_get_type(context) == SOCK_DGRAM ? 'D' :
|
|
(net_context_get_type(context) == SOCK_STREAM ? 'S' :
|
|
(net_context_get_type(context) == SOCK_RAW ? 'R' : ' ')),
|
|
net_context_get_ip_proto(context) == IPPROTO_UDP ? 'U' :
|
|
(net_context_get_ip_proto(context) == IPPROTO_TCP ? 'T' : ' '),
|
|
addr_local, addr_remote);
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
|
|
|
|
#if CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG
|
|
static void conn_handler_cb(struct net_conn *conn, void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
|
|
#define ADDR_LEN NET_IPV4_ADDR_LEN
|
|
#else
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#endif
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
/* +7 for []:port */
|
|
char addr_local[ADDR_LEN + 7];
|
|
char addr_remote[ADDR_LEN + 7] = "";
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
if (conn->local_addr.sa_family == AF_INET6) {
|
|
snprintk(addr_local, sizeof(addr_local), "[%s]:%u",
|
|
net_sprint_ipv6_addr(
|
|
&net_sin6(&conn->local_addr)->sin6_addr),
|
|
ntohs(net_sin6(&conn->local_addr)->sin6_port));
|
|
snprintk(addr_remote, sizeof(addr_remote), "[%s]:%u",
|
|
net_sprint_ipv6_addr(
|
|
&net_sin6(&conn->remote_addr)->sin6_addr),
|
|
ntohs(net_sin6(&conn->remote_addr)->sin6_port));
|
|
} else
|
|
#endif
|
|
#if defined(CONFIG_NET_IPV4)
|
|
if (conn->local_addr.sa_family == AF_INET) {
|
|
snprintk(addr_local, sizeof(addr_local), "%s:%d",
|
|
net_sprint_ipv4_addr(
|
|
&net_sin(&conn->local_addr)->sin_addr),
|
|
ntohs(net_sin(&conn->local_addr)->sin_port));
|
|
snprintk(addr_remote, sizeof(addr_remote), "%s:%d",
|
|
net_sprint_ipv4_addr(
|
|
&net_sin(&conn->remote_addr)->sin_addr),
|
|
ntohs(net_sin(&conn->remote_addr)->sin_port));
|
|
} else
|
|
#endif
|
|
if (conn->local_addr.sa_family == AF_UNSPEC) {
|
|
snprintk(addr_local, sizeof(addr_local), "AF_UNSPEC");
|
|
} else {
|
|
snprintk(addr_local, sizeof(addr_local), "AF_UNK(%d)",
|
|
conn->local_addr.sa_family);
|
|
}
|
|
|
|
PR("[%2d] %p %p\t%s\t%16s\t%16s\n",
|
|
(*count) + 1, conn, conn->cb,
|
|
net_proto2str(conn->local_addr.sa_family, conn->proto),
|
|
addr_local, addr_remote);
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG */
|
|
|
|
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
|
|
struct tcp_detail_info {
|
|
int printed_send_queue_header;
|
|
int printed_details;
|
|
int count;
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_TCP) && \
|
|
(defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE))
|
|
static void tcp_cb(struct tcp *conn, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
uint16_t recv_mss = net_tcp_get_supported_mss(conn);
|
|
|
|
PR("%p %p %5u %5u %10u %10u %5u %s\n",
|
|
conn, conn->context,
|
|
ntohs(net_sin6_ptr(&conn->context->local)->sin6_port),
|
|
ntohs(net_sin6(&conn->context->remote)->sin6_port),
|
|
conn->seq, conn->ack, recv_mss,
|
|
net_tcp_state_str(net_tcp_get_state(conn)));
|
|
|
|
(*count)++;
|
|
}
|
|
|
|
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
|
|
static void tcp_sent_list_cb(struct tcp *conn, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct tcp_detail_info *details = data->user_data;
|
|
struct net_pkt *pkt;
|
|
sys_snode_t *node;
|
|
|
|
if (conn->state != TCP_LISTEN) {
|
|
if (!details->printed_details) {
|
|
PR("\nTCP Ref Recv_win Send_win Pending "
|
|
"Unacked Flags Queue\n");
|
|
details->printed_details = true;
|
|
}
|
|
|
|
PR("%p %ld %u\t %u\t %zd\t %d\t %d/%d/%d %s\n",
|
|
conn, atomic_get(&conn->ref_count), conn->recv_win,
|
|
conn->send_win, conn->send_data_total, conn->unacked_len,
|
|
conn->in_retransmission, conn->in_connect, conn->in_close,
|
|
sys_slist_is_empty(&conn->send_queue) ? "empty" : "data");
|
|
|
|
details->count++;
|
|
}
|
|
|
|
if (sys_slist_is_empty(&conn->send_queue)) {
|
|
return;
|
|
}
|
|
|
|
if (!details->printed_send_queue_header) {
|
|
PR("\nTCP packets waiting ACK:\n");
|
|
PR("TCP net_pkt[ref/totlen]->net_buf[ref/len]..."
|
|
"\n");
|
|
}
|
|
|
|
PR("%p ", conn);
|
|
|
|
node = sys_slist_peek_head(&conn->send_queue);
|
|
if (node) {
|
|
pkt = CONTAINER_OF(node, struct net_pkt, next);
|
|
if (pkt) {
|
|
struct net_buf *frag = pkt->frags;
|
|
|
|
if (!details->printed_send_queue_header) {
|
|
PR("%p[%ld/%zd]", pkt,
|
|
atomic_get(&pkt->atomic_ref),
|
|
net_pkt_get_len(pkt));
|
|
details->printed_send_queue_header = true;
|
|
} else {
|
|
PR(" %p[%ld/%zd]",
|
|
pkt, atomic_get(&pkt->atomic_ref),
|
|
net_pkt_get_len(pkt));
|
|
}
|
|
|
|
if (frag) {
|
|
PR("->");
|
|
}
|
|
|
|
while (frag) {
|
|
PR("%p[%d/%d]", frag, frag->ref, frag->len);
|
|
|
|
frag = frag->frags;
|
|
if (frag) {
|
|
PR("->");
|
|
}
|
|
}
|
|
|
|
PR("\n");
|
|
}
|
|
}
|
|
|
|
details->printed_send_queue_header = true;
|
|
}
|
|
#endif /* CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG */
|
|
#endif /* TCP */
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
static void ipv6_frag_cb(struct net_ipv6_reassembly *reass,
|
|
void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
char src[ADDR_LEN];
|
|
int i;
|
|
|
|
if (!*count) {
|
|
PR("\nIPv6 reassembly Id Remain "
|
|
"Src \tDst\n");
|
|
}
|
|
|
|
snprintk(src, ADDR_LEN, "%s", net_sprint_ipv6_addr(&reass->src));
|
|
|
|
PR("%p 0x%08x %5d %16s\t%16s\n", reass, reass->id,
|
|
k_ticks_to_ms_ceil32(k_work_delayable_remaining_get(&reass->timer)),
|
|
src, net_sprint_ipv6_addr(&reass->dst));
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_FRAGMENT_MAX_PKT; i++) {
|
|
if (reass->pkt[i]) {
|
|
struct net_buf *frag = reass->pkt[i]->frags;
|
|
|
|
PR("[%d] pkt %p->", i, reass->pkt[i]);
|
|
|
|
while (frag) {
|
|
PR("%p", frag);
|
|
|
|
frag = frag->frags;
|
|
if (frag) {
|
|
PR("->");
|
|
}
|
|
}
|
|
|
|
PR("\n");
|
|
}
|
|
}
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_IPV6_FRAGMENT */
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC)
|
|
static void allocs_cb(struct net_pkt *pkt,
|
|
struct net_buf *buf,
|
|
const char *func_alloc,
|
|
int line_alloc,
|
|
const char *func_free,
|
|
int line_free,
|
|
bool in_use,
|
|
void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
const char *str;
|
|
|
|
if (in_use) {
|
|
str = "used";
|
|
} else {
|
|
if (func_alloc) {
|
|
str = "free";
|
|
} else {
|
|
str = "avail";
|
|
}
|
|
}
|
|
|
|
if (buf) {
|
|
goto buf;
|
|
}
|
|
|
|
if (func_alloc) {
|
|
if (in_use) {
|
|
PR("%p/%ld\t%5s\t%5s\t%s():%d\n",
|
|
pkt, atomic_get(&pkt->atomic_ref), str,
|
|
net_pkt_slab2str(pkt->slab),
|
|
func_alloc, line_alloc);
|
|
} else {
|
|
PR("%p\t%5s\t%5s\t%s():%d -> %s():%d\n",
|
|
pkt, str, net_pkt_slab2str(pkt->slab),
|
|
func_alloc, line_alloc, func_free,
|
|
line_free);
|
|
}
|
|
}
|
|
|
|
return;
|
|
buf:
|
|
if (func_alloc) {
|
|
struct net_buf_pool *pool = net_buf_pool_get(buf->pool_id);
|
|
|
|
if (in_use) {
|
|
PR("%p/%d\t%5s\t%5s\t%s():%d\n",
|
|
buf, buf->ref,
|
|
str, net_pkt_pool2str(pool), func_alloc,
|
|
line_alloc);
|
|
} else {
|
|
PR("%p\t%5s\t%5s\t%s():%d -> %s():%d\n",
|
|
buf, str, net_pkt_pool2str(pool),
|
|
func_alloc, line_alloc, func_free,
|
|
line_free);
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_DEBUG_NET_PKT_ALLOC */
|
|
|
|
/* Put the actual shell commands after this */
|
|
|
|
static int cmd_net_allocs(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC)
|
|
struct net_shell_user_data user_data;
|
|
#endif
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_PKT_ALLOC)
|
|
user_data.shell = shell;
|
|
|
|
PR("Network memory allocations\n\n");
|
|
PR("memory\t\tStatus\tPool\tFunction alloc -> freed\n");
|
|
net_pkt_allocs_foreach(allocs_cb, &user_data);
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_DEBUG_NET_PKT_ALLOC", "net_pkt allocation");
|
|
#endif /* CONFIG_NET_DEBUG_NET_PKT_ALLOC */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ARP) && defined(CONFIG_NET_NATIVE)
|
|
static void arp_cb(struct arp_entry *entry, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
|
|
if (*count == 0) {
|
|
PR(" Interface Link Address\n");
|
|
}
|
|
|
|
PR("[%2d] %d %s %s\n", *count,
|
|
net_if_get_by_iface(entry->iface),
|
|
net_sprint_ll_addr(entry->eth.addr, sizeof(struct net_eth_addr)),
|
|
net_sprint_ipv4_addr(&entry->ip));
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_ARP */
|
|
|
|
#if !defined(CONFIG_NET_ARP)
|
|
static void print_arp_error(const struct shell *shell)
|
|
{
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_NATIVE, CONFIG_NET_ARP, CONFIG_NET_IPV4 and"
|
|
" CONFIG_NET_L2_ETHERNET", "ARP");
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_arp(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_ARP)
|
|
struct net_shell_user_data user_data;
|
|
int arg = 1;
|
|
#endif
|
|
|
|
ARG_UNUSED(argc);
|
|
|
|
#if defined(CONFIG_NET_ARP)
|
|
if (!argv[arg]) {
|
|
/* ARP cache content */
|
|
int count = 0;
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
if (net_arp_foreach(arp_cb, &user_data) == 0) {
|
|
PR("ARP cache is empty.\n");
|
|
}
|
|
}
|
|
#else
|
|
print_arp_error(shell);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_arp_flush(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_ARP)
|
|
PR("Flushing ARP cache.\n");
|
|
net_arp_clear_cache(NULL);
|
|
#else
|
|
print_arp_error(shell);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
static const struct device *capture_dev;
|
|
|
|
static void get_address_str(const struct sockaddr *addr,
|
|
char *str, int str_len)
|
|
{
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && addr->sa_family == AF_INET6) {
|
|
snprintk(str, str_len, "[%s]:%u",
|
|
net_sprint_ipv6_addr(&net_sin6(addr)->sin6_addr),
|
|
ntohs(net_sin6(addr)->sin6_port));
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) && addr->sa_family == AF_INET) {
|
|
snprintk(str, str_len, "%s:%d",
|
|
net_sprint_ipv4_addr(&net_sin(addr)->sin_addr),
|
|
ntohs(net_sin(addr)->sin_port));
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
|
|
addr->sa_family == AF_PACKET) {
|
|
snprintk(str, str_len, "AF_PACKET");
|
|
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_CAN) &&
|
|
addr->sa_family == AF_CAN) {
|
|
snprintk(str, str_len, "AF_CAN");
|
|
} else if (addr->sa_family == AF_UNSPEC) {
|
|
snprintk(str, str_len, "AF_UNSPEC");
|
|
} else {
|
|
snprintk(str, str_len, "AF_UNK(%d)", addr->sa_family);
|
|
}
|
|
}
|
|
|
|
static void capture_cb(struct net_capture_info *info, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
char addr_local[ADDR_LEN + 7];
|
|
char addr_peer[ADDR_LEN + 7];
|
|
|
|
if (*count == 0) {
|
|
PR(" \t\tCapture Tunnel\n");
|
|
PR("Device\t\tiface iface Local\t\t\tPeer\n");
|
|
}
|
|
|
|
get_address_str(info->local, addr_local, sizeof(addr_local));
|
|
get_address_str(info->peer, addr_peer, sizeof(addr_peer));
|
|
|
|
PR("%s\t%c %d %s\t%s\n", info->capture_dev->name,
|
|
info->is_enabled ?
|
|
(net_if_get_by_iface(info->capture_iface) + '0') : '-',
|
|
net_if_get_by_iface(info->tunnel_iface),
|
|
addr_local, addr_peer);
|
|
|
|
(*count)++;
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_capture(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
bool ret;
|
|
|
|
if (capture_dev == NULL) {
|
|
PR_INFO("Network packet capture %s\n", "not configured");
|
|
} else {
|
|
struct net_shell_user_data user_data;
|
|
int count = 0;
|
|
|
|
ret = net_capture_is_enabled(capture_dev);
|
|
PR_INFO("Network packet capture %s\n",
|
|
ret ? "enabled" : "disabled");
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
net_capture_foreach(capture_cb, &user_data);
|
|
}
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_CAPTURE", "network packet capture");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_capture_setup(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
int ret, arg = 1;
|
|
const char *remote, *local, *peer;
|
|
|
|
remote = argv[arg++];
|
|
if (!remote) {
|
|
PR_WARNING("Remote IP address not specified.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
local = argv[arg++];
|
|
if (!local) {
|
|
PR_WARNING("Local IP address not specified.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
peer = argv[arg];
|
|
if (!peer) {
|
|
PR_WARNING("Peer IP address not specified.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (capture_dev != NULL) {
|
|
PR_INFO("Capture already setup, cleaning up settings.\n");
|
|
net_capture_cleanup(capture_dev);
|
|
capture_dev = NULL;
|
|
}
|
|
|
|
ret = net_capture_setup(remote, local, peer, &capture_dev);
|
|
if (ret < 0) {
|
|
PR_WARNING("Capture cannot be setup (%d)\n", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
PR_INFO("Capture setup done, next enable it by "
|
|
"\"net capture enable <idx>\"\n");
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_CAPTURE", "network packet capture");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_capture_cleanup(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
int ret;
|
|
|
|
if (capture_dev == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ret = net_capture_cleanup(capture_dev);
|
|
if (ret < 0) {
|
|
PR_WARNING("Capture %s failed (%d)\n", "cleanup", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
capture_dev = NULL;
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_CAPTURE", "network packet capture");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_capture_enable(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
int ret, arg = 1, if_index;
|
|
struct net_if *iface;
|
|
|
|
if (capture_dev == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (argv[arg] == NULL) {
|
|
PR_WARNING("Interface index is missing. Please give interface "
|
|
"what you want to monitor\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if_index = atoi(argv[arg++]);
|
|
if (if_index == 0) {
|
|
PR_WARNING("Interface index %d is invalid.\n", if_index);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
iface = net_if_get_by_index(if_index);
|
|
if (iface == NULL) {
|
|
PR_WARNING("No such interface with index %d\n", if_index);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_capture_enable(capture_dev, iface);
|
|
if (ret < 0) {
|
|
PR_WARNING("Capture %s failed (%d)\n", "enable", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_CAPTURE", "network packet capture");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_capture_disable(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_CAPTURE)
|
|
int ret;
|
|
|
|
if (capture_dev == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ret = net_capture_disable(capture_dev);
|
|
if (ret < 0) {
|
|
PR_WARNING("Capture %s failed (%d)\n", "disable", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_CAPTURE", "network packet capture");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_conn(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
|
|
struct net_shell_user_data user_data;
|
|
int count = 0;
|
|
|
|
PR(" Context \tIface Flags Local Remote\n");
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
net_context_foreach(context_cb, &user_data);
|
|
|
|
if (count == 0) {
|
|
PR("No connections\n");
|
|
}
|
|
|
|
#if CONFIG_NET_CONN_LOG_LEVEL >= LOG_LEVEL_DBG
|
|
PR("\n Handler Callback \tProto\tLocal \tRemote\n");
|
|
|
|
count = 0;
|
|
|
|
net_conn_foreach(conn_handler_cb, &user_data);
|
|
|
|
if (count == 0) {
|
|
PR("No connection handlers found.\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
PR("\nTCP Context Src port Dst port "
|
|
"Send-Seq Send-Ack MSS State\n");
|
|
|
|
count = 0;
|
|
|
|
net_tcp_foreach(tcp_cb, &user_data);
|
|
|
|
if (count == 0) {
|
|
PR("No TCP connections\n");
|
|
} else {
|
|
#if CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG
|
|
/* Print information about pending packets */
|
|
struct tcp_detail_info details;
|
|
|
|
count = 0;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_TCP)) {
|
|
memset(&details, 0, sizeof(details));
|
|
user_data.user_data = &details;
|
|
}
|
|
|
|
net_tcp_foreach(tcp_sent_list_cb, &user_data);
|
|
|
|
if (IS_ENABLED(CONFIG_NET_TCP)) {
|
|
if (details.count == 0) {
|
|
PR("No active connections.\n");
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_TCP_LOG_LEVEL >= LOG_LEVEL_DBG */
|
|
}
|
|
|
|
#if CONFIG_NET_TCP_LOG_LEVEL < LOG_LEVEL_DBG
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_TCP_LOG_LEVEL_DBG", "TCP debugging");
|
|
#endif /* CONFIG_NET_TCP_LOG_LEVEL < LOG_LEVEL_DBG */
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
count = 0;
|
|
|
|
net_ipv6_frag_foreach(ipv6_frag_cb, &user_data);
|
|
|
|
/* Do not print anything if no fragments are pending atm */
|
|
#endif
|
|
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_OFFLOAD or CONFIG_NET_NATIVE",
|
|
"connection information");
|
|
|
|
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
static void dns_result_cb(enum dns_resolve_status status,
|
|
struct dns_addrinfo *info,
|
|
void *user_data)
|
|
{
|
|
const struct shell *shell = user_data;
|
|
|
|
if (status == DNS_EAI_CANCELED) {
|
|
PR_WARNING("dns: Timeout while resolving name.\n");
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_INPROGRESS && info) {
|
|
char addr[NET_IPV6_ADDR_LEN];
|
|
|
|
if (info->ai_family == AF_INET) {
|
|
net_addr_ntop(AF_INET,
|
|
&net_sin(&info->ai_addr)->sin_addr,
|
|
addr, NET_IPV4_ADDR_LEN);
|
|
} else if (info->ai_family == AF_INET6) {
|
|
net_addr_ntop(AF_INET6,
|
|
&net_sin6(&info->ai_addr)->sin6_addr,
|
|
addr, NET_IPV6_ADDR_LEN);
|
|
} else {
|
|
strncpy(addr, "Invalid protocol family",
|
|
sizeof(addr));
|
|
/* strncpy() doesn't guarantee NUL byte at the end. */
|
|
addr[sizeof(addr) - 1] = 0;
|
|
}
|
|
|
|
PR("dns: %s\n", addr);
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_ALLDONE) {
|
|
PR("dns: All results received\n");
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_FAIL) {
|
|
PR_WARNING("dns: No such name found.\n");
|
|
return;
|
|
}
|
|
|
|
PR_WARNING("dns: Unhandled status %d received\n", status);
|
|
}
|
|
|
|
static void print_dns_info(const struct shell *shell,
|
|
struct dns_resolve_context *ctx)
|
|
{
|
|
int i;
|
|
|
|
PR("DNS servers:\n");
|
|
|
|
for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS +
|
|
DNS_MAX_MCAST_SERVERS; i++) {
|
|
if (ctx->servers[i].dns_server.sa_family == AF_INET) {
|
|
PR("\t%s:%u\n",
|
|
net_sprint_ipv4_addr(
|
|
&net_sin(&ctx->servers[i].dns_server)->
|
|
sin_addr),
|
|
ntohs(net_sin(
|
|
&ctx->servers[i].dns_server)->sin_port));
|
|
} else if (ctx->servers[i].dns_server.sa_family == AF_INET6) {
|
|
PR("\t[%s]:%u\n",
|
|
net_sprint_ipv6_addr(
|
|
&net_sin6(&ctx->servers[i].dns_server)->
|
|
sin6_addr),
|
|
ntohs(net_sin6(
|
|
&ctx->servers[i].dns_server)->sin6_port));
|
|
}
|
|
}
|
|
|
|
PR("Pending queries:\n");
|
|
|
|
for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
|
|
int32_t remaining;
|
|
|
|
if (!ctx->queries[i].cb || !ctx->queries[i].query) {
|
|
continue;
|
|
}
|
|
|
|
remaining = k_ticks_to_ms_ceil32(
|
|
k_work_delayable_remaining_get(&ctx->queries[i].timer));
|
|
|
|
if (ctx->queries[i].query_type == DNS_QUERY_TYPE_A) {
|
|
PR("\tIPv4[%u]: %s remaining %d\n",
|
|
ctx->queries[i].id,
|
|
ctx->queries[i].query,
|
|
remaining);
|
|
} else if (ctx->queries[i].query_type == DNS_QUERY_TYPE_AAAA) {
|
|
PR("\tIPv6[%u]: %s remaining %d\n",
|
|
ctx->queries[i].id,
|
|
ctx->queries[i].query,
|
|
remaining);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_dns_cancel(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
struct dns_resolve_context *ctx;
|
|
int ret, i;
|
|
#endif
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
ctx = dns_resolve_get_default();
|
|
if (!ctx) {
|
|
PR_WARNING("No default DNS context found.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
for (ret = 0, i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
|
|
if (!ctx->queries[i].cb) {
|
|
continue;
|
|
}
|
|
|
|
if (!dns_resolve_cancel(ctx, ctx->queries[i].id)) {
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
PR("Cancelled %d pending requests.\n", ret);
|
|
} else {
|
|
PR("No pending DNS requests.\n");
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_DNS_RESOLVER",
|
|
"DNS resolver");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_dns_query(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
#define DNS_TIMEOUT (MSEC_PER_SEC * 2) /* ms */
|
|
enum dns_query_type qtype = DNS_QUERY_TYPE_A;
|
|
char *host, *type = NULL;
|
|
int ret, arg = 1;
|
|
|
|
host = argv[arg++];
|
|
if (!host) {
|
|
PR_WARNING("Hostname not specified.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (argv[arg]) {
|
|
type = argv[arg];
|
|
}
|
|
|
|
if (type) {
|
|
if (strcmp(type, "A") == 0) {
|
|
qtype = DNS_QUERY_TYPE_A;
|
|
PR("IPv4 address type\n");
|
|
} else if (strcmp(type, "AAAA") == 0) {
|
|
qtype = DNS_QUERY_TYPE_AAAA;
|
|
PR("IPv6 address type\n");
|
|
} else {
|
|
PR_WARNING("Unknown query type, specify either "
|
|
"A or AAAA\n");
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
ret = dns_get_addr_info(host, qtype, NULL, dns_result_cb,
|
|
(void *)shell, DNS_TIMEOUT);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot resolve '%s' (%d)\n", host, ret);
|
|
} else {
|
|
PR("Query for '%s' sent.\n", host);
|
|
}
|
|
#else
|
|
PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
|
|
"enable it.\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_dns(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
struct dns_resolve_context *ctx;
|
|
#endif
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
if (argv[1]) {
|
|
/* So this is a query then */
|
|
cmd_net_dns_query(shell, argc, argv);
|
|
return 0;
|
|
}
|
|
|
|
/* DNS status */
|
|
ctx = dns_resolve_get_default();
|
|
if (!ctx) {
|
|
PR_WARNING("No default DNS context found.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
print_dns_info(shell, ctx);
|
|
#else
|
|
PR_INFO("DNS resolver not supported. Set CONFIG_DNS_RESOLVER to "
|
|
"enable it.\n");
|
|
#endif
|
|
|
|
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);
|
|
|
|
static void gptp_port_cb(int port, struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
|
|
if (*count == 0) {
|
|
PR("Port Interface \tRole\n");
|
|
}
|
|
|
|
(*count)++;
|
|
|
|
PR("%2d %p [%d] \t%s\n", port, iface, net_if_get_by_iface(iface),
|
|
selected_role_str(port));
|
|
}
|
|
|
|
static const char *pdelay_req2str(enum gptp_pdelay_req_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PDELAY_REQ_NOT_ENABLED:
|
|
return "REQ_NOT_ENABLED";
|
|
case GPTP_PDELAY_REQ_INITIAL_SEND_REQ:
|
|
return "INITIAL_SEND_REQ";
|
|
case GPTP_PDELAY_REQ_RESET:
|
|
return "REQ_RESET";
|
|
case GPTP_PDELAY_REQ_SEND_REQ:
|
|
return "SEND_REQ";
|
|
case GPTP_PDELAY_REQ_WAIT_RESP:
|
|
return "WAIT_RESP";
|
|
case GPTP_PDELAY_REQ_WAIT_FOLLOW_UP:
|
|
return "WAIT_FOLLOW_UP";
|
|
case GPTP_PDELAY_REQ_WAIT_ITV_TIMER:
|
|
return "WAIT_ITV_TIMER";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *pdelay_resp2str(enum gptp_pdelay_resp_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PDELAY_RESP_NOT_ENABLED:
|
|
return "RESP_NOT_ENABLED";
|
|
case GPTP_PDELAY_RESP_INITIAL_WAIT_REQ:
|
|
return "INITIAL_WAIT_REQ";
|
|
case GPTP_PDELAY_RESP_WAIT_REQ:
|
|
return "WAIT_REQ";
|
|
case GPTP_PDELAY_RESP_WAIT_TSTAMP:
|
|
return "WAIT_TSTAMP";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *sync_rcv2str(enum gptp_sync_rcv_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_SYNC_RCV_DISCARD:
|
|
return "DISCARD";
|
|
case GPTP_SYNC_RCV_WAIT_SYNC:
|
|
return "WAIT_SYNC";
|
|
case GPTP_SYNC_RCV_WAIT_FOLLOW_UP:
|
|
return "WAIT_FOLLOW_UP";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *sync_send2str(enum gptp_sync_send_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_SYNC_SEND_INITIALIZING:
|
|
return "INITIALIZING";
|
|
case GPTP_SYNC_SEND_SEND_SYNC:
|
|
return "SEND_SYNC";
|
|
case GPTP_SYNC_SEND_SEND_FUP:
|
|
return "SEND_FUP";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *pss_rcv2str(enum gptp_pss_rcv_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PSS_RCV_DISCARD:
|
|
return "DISCARD";
|
|
case GPTP_PSS_RCV_RECEIVED_SYNC:
|
|
return "RECEIVED_SYNC";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *pss_send2str(enum gptp_pss_send_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PSS_SEND_TRANSMIT_INIT:
|
|
return "TRANSMIT_INIT";
|
|
case GPTP_PSS_SEND_SYNC_RECEIPT_TIMEOUT:
|
|
return "SYNC_RECEIPT_TIMEOUT";
|
|
case GPTP_PSS_SEND_SEND_MD_SYNC:
|
|
return "SEND_MD_SYNC";
|
|
case GPTP_PSS_SEND_SET_SYNC_RECEIPT_TIMEOUT:
|
|
return "SET_SYNC_RECEIPT_TIMEOUT";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *pa_rcv2str(enum gptp_pa_rcv_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PA_RCV_DISCARD:
|
|
return "DISCARD";
|
|
case GPTP_PA_RCV_RECEIVE:
|
|
return "RECEIVE";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *pa_info2str(enum gptp_pa_info_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PA_INFO_DISABLED:
|
|
return "DISABLED";
|
|
case GPTP_PA_INFO_POST_DISABLED:
|
|
return "POST_DISABLED";
|
|
case GPTP_PA_INFO_AGED:
|
|
return "AGED";
|
|
case GPTP_PA_INFO_UPDATE:
|
|
return "UPDATE";
|
|
case GPTP_PA_INFO_CURRENT:
|
|
return "CURRENT";
|
|
case GPTP_PA_INFO_RECEIVE:
|
|
return "RECEIVE";
|
|
case GPTP_PA_INFO_SUPERIOR_MASTER_PORT:
|
|
return "SUPERIOR_MASTER_PORT";
|
|
case GPTP_PA_INFO_REPEATED_MASTER_PORT:
|
|
return "REPEATED_MASTER_PORT";
|
|
case GPTP_PA_INFO_INFERIOR_MASTER_OR_OTHER_PORT:
|
|
return "INFERIOR_MASTER_OR_OTHER_PORT";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *pa_transmit2str(enum gptp_pa_transmit_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PA_TRANSMIT_INIT:
|
|
return "INIT";
|
|
case GPTP_PA_TRANSMIT_PERIODIC:
|
|
return "PERIODIC";
|
|
case GPTP_PA_TRANSMIT_IDLE:
|
|
return "IDLE";
|
|
case GPTP_PA_TRANSMIT_POST_IDLE:
|
|
return "POST_IDLE";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *site_sync2str(enum gptp_site_sync_sync_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_SSS_INITIALIZING:
|
|
return "INITIALIZING";
|
|
case GPTP_SSS_RECEIVING_SYNC:
|
|
return "RECEIVING_SYNC";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static const char *clk_slave2str(enum gptp_clk_slave_sync_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_CLK_SLAVE_SYNC_INITIALIZING:
|
|
return "INITIALIZING";
|
|
case GPTP_CLK_SLAVE_SYNC_SEND_SYNC_IND:
|
|
return "SEND_SYNC_IND";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *pr_selection2str(enum gptp_pr_selection_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_PR_SELECTION_INIT_BRIDGE:
|
|
return "INIT_BRIDGE";
|
|
case GPTP_PR_SELECTION_ROLE_SELECTION:
|
|
return "ROLE_SELECTION";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
static const char *cms_rcv2str(enum gptp_cms_rcv_states state)
|
|
{
|
|
switch (state) {
|
|
case GPTP_CMS_RCV_INITIALIZING:
|
|
return "INITIALIZING";
|
|
case GPTP_CMS_RCV_WAITING:
|
|
return "WAITING";
|
|
case GPTP_CMS_RCV_SOURCE_TIME:
|
|
return "SOURCE_TIME";
|
|
}
|
|
|
|
return "<unknown>";
|
|
};
|
|
|
|
#if !defined(USCALED_NS_TO_NS)
|
|
#define USCALED_NS_TO_NS(val) (val >> 16)
|
|
#endif
|
|
|
|
static const char *selected_role_str(int port)
|
|
{
|
|
switch (GPTP_GLOBAL_DS()->selected_role[port]) {
|
|
case GPTP_PORT_INITIALIZING:
|
|
return "INITIALIZING";
|
|
case GPTP_PORT_FAULTY:
|
|
return "FAULTY";
|
|
case GPTP_PORT_DISABLED:
|
|
return "DISABLED";
|
|
case GPTP_PORT_LISTENING:
|
|
return "LISTENING";
|
|
case GPTP_PORT_PRE_MASTER:
|
|
return "PRE-MASTER";
|
|
case GPTP_PORT_MASTER:
|
|
return "MASTER";
|
|
case GPTP_PORT_PASSIVE:
|
|
return "PASSIVE";
|
|
case GPTP_PORT_UNCALIBRATED:
|
|
return "UNCALIBRATED";
|
|
case GPTP_PORT_SLAVE:
|
|
return "SLAVE";
|
|
}
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
static void gptp_print_port_info(const struct shell *shell, int port)
|
|
{
|
|
struct gptp_port_bmca_data *port_bmca_data;
|
|
struct gptp_port_param_ds *port_param_ds;
|
|
struct gptp_port_states *port_state;
|
|
struct gptp_domain *gptp_domain;
|
|
struct gptp_port_ds *port_ds;
|
|
struct net_if *iface;
|
|
int ret, i;
|
|
|
|
gptp_domain = gptp_get_domain();
|
|
|
|
ret = gptp_get_port_data(gptp_domain,
|
|
port,
|
|
&port_ds,
|
|
&port_param_ds,
|
|
&port_state,
|
|
&port_bmca_data,
|
|
&iface);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot get gPTP information for port %d (%d)\n",
|
|
port, ret);
|
|
return;
|
|
}
|
|
|
|
NET_ASSERT(port == port_ds->port_id.port_number,
|
|
"Port number mismatch! (%d vs %d)", port,
|
|
port_ds->port_id.port_number);
|
|
|
|
PR("Port id : %d (%s)\n", port_ds->port_id.port_number,
|
|
selected_role_str(port_ds->port_id.port_number));
|
|
PR("Interface : %p [%d]\n", iface, net_if_get_by_iface(iface));
|
|
PR("Clock id : ");
|
|
for (i = 0; i < sizeof(port_ds->port_id.clk_id); i++) {
|
|
PR("%02x", port_ds->port_id.clk_id[i]);
|
|
|
|
if (i != (sizeof(port_ds->port_id.clk_id) - 1)) {
|
|
PR(":");
|
|
}
|
|
}
|
|
PR("\n");
|
|
|
|
PR("Version : %d\n", port_ds->version);
|
|
PR("AS capable : %s\n", port_ds->as_capable ? "yes" : "no");
|
|
|
|
PR("\nConfiguration:\n");
|
|
PR("Time synchronization and Best Master Selection enabled "
|
|
": %s\n", port_ds->ptt_port_enabled ? "yes" : "no");
|
|
PR("The port is measuring the path delay "
|
|
": %s\n", port_ds->is_measuring_delay ? "yes" : "no");
|
|
PR("One way propagation time on %s : %u ns\n",
|
|
"the link attached to this port",
|
|
(uint32_t)port_ds->neighbor_prop_delay);
|
|
PR("Propagation time threshold for %s : %u ns\n",
|
|
"the link attached to this port",
|
|
(uint32_t)port_ds->neighbor_prop_delay_thresh);
|
|
PR("Estimate of the ratio of the frequency with the peer "
|
|
": %u\n", (uint32_t)port_ds->neighbor_rate_ratio);
|
|
PR("Asymmetry on the link relative to the grand master time base "
|
|
": %" PRId64 "\n", port_ds->delay_asymmetry);
|
|
PR("Maximum interval between sync %s "
|
|
": %" PRIu64 "\n", "messages",
|
|
port_ds->sync_receipt_timeout_time_itv);
|
|
PR("Maximum number of Path Delay Requests without a response "
|
|
": %d\n", port_ds->allowed_lost_responses);
|
|
PR("Current Sync %s : %d\n",
|
|
"sequence id for this port", port_ds->sync_seq_id);
|
|
PR("Current Path Delay Request %s : %d\n",
|
|
"sequence id for this port", port_ds->pdelay_req_seq_id);
|
|
PR("Current Announce %s : %d\n",
|
|
"sequence id for this port", port_ds->announce_seq_id);
|
|
PR("Current Signaling %s : %d\n",
|
|
"sequence id for this port", port_ds->signaling_seq_id);
|
|
PR("Whether neighborRateRatio %s : %s\n",
|
|
"needs to be computed for this port",
|
|
port_ds->compute_neighbor_rate_ratio ? "yes" : "no");
|
|
PR("Whether neighborPropDelay %s : %s\n",
|
|
"needs to be computed for this port",
|
|
port_ds->compute_neighbor_prop_delay ? "yes" : "no");
|
|
PR("Initial Announce Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->ini_log_announce_itv);
|
|
PR("Current Announce Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->cur_log_announce_itv);
|
|
PR("Initial Sync Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->ini_log_half_sync_itv);
|
|
PR("Current Sync Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->cur_log_half_sync_itv);
|
|
PR("Initial Path Delay Request Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->ini_log_pdelay_req_itv);
|
|
PR("Current Path Delay Request Interval %s : %d\n",
|
|
"as a Logarithm to base 2", port_ds->cur_log_pdelay_req_itv);
|
|
PR("Time without receiving announce %s %s : %d ms (%d)\n",
|
|
"messages", "before running BMCA",
|
|
gptp_uscaled_ns_to_timer_ms(
|
|
&port_bmca_data->ann_rcpt_timeout_time_interval),
|
|
port_ds->announce_receipt_timeout);
|
|
PR("Time without receiving sync %s %s : %" PRIu64 " ms (%d)\n",
|
|
"messages", "before running BMCA",
|
|
(port_ds->sync_receipt_timeout_time_itv >> 16) /
|
|
(NSEC_PER_SEC / MSEC_PER_SEC),
|
|
port_ds->sync_receipt_timeout);
|
|
PR("Sync event %s : %" PRIu64 " ms\n",
|
|
"transmission interval for the port",
|
|
USCALED_NS_TO_NS(port_ds->half_sync_itv.low) /
|
|
(NSEC_PER_USEC * USEC_PER_MSEC));
|
|
PR("Path Delay Request %s : %" PRIu64 " ms\n",
|
|
"transmission interval for the port",
|
|
USCALED_NS_TO_NS(port_ds->pdelay_req_itv.low) /
|
|
(NSEC_PER_USEC * USEC_PER_MSEC));
|
|
PR("BMCA %s %s%d%s: %d\n", "default", "priority", 1,
|
|
" ",
|
|
gptp_domain->default_ds.priority1);
|
|
PR("BMCA %s %s%d%s: %d\n", "default", "priority", 2,
|
|
" ",
|
|
gptp_domain->default_ds.priority2);
|
|
|
|
PR("\nRuntime status:\n");
|
|
PR("Current global port state "
|
|
" : %s\n", selected_role_str(port));
|
|
PR("Path Delay Request state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pdelay_req2str(port_state->pdelay_req.state));
|
|
PR("\tInitial Path Delay Response Peer Timestamp "
|
|
": %" PRIu64 "\n", port_state->pdelay_req.ini_resp_evt_tstamp);
|
|
PR("\tInitial Path Delay Response Ingress Timestamp "
|
|
": %" PRIu64 "\n", port_state->pdelay_req.ini_resp_ingress_tstamp);
|
|
PR("\tPath Delay Response %s %s : %u\n",
|
|
"messages", "received",
|
|
port_state->pdelay_req.rcvd_pdelay_resp);
|
|
PR("\tPath Delay Follow Up %s %s : %u\n",
|
|
"messages", "received",
|
|
port_state->pdelay_req.rcvd_pdelay_follow_up);
|
|
PR("\tNumber of lost Path Delay Responses "
|
|
": %u\n", port_state->pdelay_req.lost_responses);
|
|
PR("\tTimer expired send a new Path Delay Request "
|
|
": %u\n", port_state->pdelay_req.pdelay_timer_expired);
|
|
PR("\tNeighborRateRatio has been computed successfully "
|
|
": %u\n", port_state->pdelay_req.neighbor_rate_ratio_valid);
|
|
PR("\tPath Delay has already been computed after init "
|
|
": %u\n", port_state->pdelay_req.init_pdelay_compute);
|
|
PR("\tCount consecutive reqs with multiple responses "
|
|
": %u\n", port_state->pdelay_req.multiple_resp_count);
|
|
|
|
PR("Path Delay Response state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pdelay_resp2str(port_state->pdelay_resp.state));
|
|
|
|
PR("SyncReceive state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", sync_rcv2str(port_state->sync_rcv.state));
|
|
PR("\tA Sync %s %s : %s\n",
|
|
"Message", "has been received",
|
|
port_state->sync_rcv.rcvd_sync ? "yes" : "no");
|
|
PR("\tA Follow Up %s %s : %s\n",
|
|
"Message", "has been received",
|
|
port_state->sync_rcv.rcvd_follow_up ? "yes" : "no");
|
|
PR("\tA Follow Up %s %s : %s\n",
|
|
"Message", "timeout",
|
|
port_state->sync_rcv.follow_up_timeout_expired ? "yes" : "no");
|
|
PR("\tTime at which a Sync %s without Follow Up\n"
|
|
"\t will be discarded "
|
|
": %" PRIu64 "\n", "Message",
|
|
port_state->sync_rcv.follow_up_receipt_timeout);
|
|
|
|
PR("SyncSend state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", sync_send2str(port_state->sync_send.state));
|
|
PR("\tA MDSyncSend structure %s : %s\n",
|
|
"has been received",
|
|
port_state->sync_send.rcvd_md_sync ? "yes" : "no");
|
|
PR("\tThe timestamp for the sync msg %s : %s\n",
|
|
"has been received",
|
|
port_state->sync_send.md_sync_timestamp_avail ? "yes" : "no");
|
|
|
|
PR("PortSyncSyncReceive state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pss_rcv2str(port_state->pss_rcv.state));
|
|
PR("\tGrand Master / Local Clock frequency ratio "
|
|
": %f\n", port_state->pss_rcv.rate_ratio);
|
|
PR("\tA MDSyncReceive struct is ready to be processed "
|
|
": %s\n", port_state->pss_rcv.rcvd_md_sync ? "yes" : "no");
|
|
PR("\tExpiry of SyncReceiptTimeoutTimer : %s\n",
|
|
port_state->pss_rcv.rcv_sync_receipt_timeout_timer_expired ?
|
|
"yes" : "no");
|
|
|
|
PR("PortSyncSyncSend state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pss_send2str(port_state->pss_send.state));
|
|
PR("\tFollow Up Correction Field of last recv PSS "
|
|
": %" PRId64 "\n",
|
|
port_state->pss_send.last_follow_up_correction_field);
|
|
PR("\tUpstream Tx Time of the last recv PortSyncSync "
|
|
": %" PRIu64 "\n", port_state->pss_send.last_upstream_tx_time);
|
|
PR("\tRate Ratio of the last received PortSyncSync "
|
|
": %f\n",
|
|
port_state->pss_send.last_rate_ratio);
|
|
PR("\tGM Freq Change of the last received PortSyncSync "
|
|
": %f\n", port_state->pss_send.last_gm_freq_change);
|
|
PR("\tGM Time Base Indicator of last recv PortSyncSync "
|
|
": %d\n", port_state->pss_send.last_gm_time_base_indicator);
|
|
PR("\tReceived Port Number of last recv PortSyncSync "
|
|
": %d\n",
|
|
port_state->pss_send.last_rcvd_port_num);
|
|
PR("\tPortSyncSync structure is ready to be processed "
|
|
": %s\n", port_state->pss_send.rcvd_pss_sync ? "yes" : "no");
|
|
PR("\tFlag when the %s has expired : %s\n",
|
|
"half_sync_itv_timer",
|
|
port_state->pss_send.half_sync_itv_timer_expired ? "yes" : "no");
|
|
PR("\tHas %s expired twice : %s\n",
|
|
"half_sync_itv_timer",
|
|
port_state->pss_send.sync_itv_timer_expired ? "yes" : "no");
|
|
PR("\tHas syncReceiptTimeoutTime expired "
|
|
": %s\n",
|
|
port_state->pss_send.send_sync_receipt_timeout_timer_expired ?
|
|
"yes" : "no");
|
|
|
|
PR("PortAnnounceReceive state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pa_rcv2str(port_state->pa_rcv.state));
|
|
PR("\tAn announce message is ready to be processed "
|
|
": %s\n",
|
|
port_state->pa_rcv.rcvd_announce ? "yes" : "no");
|
|
|
|
PR("PortAnnounceInformation state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pa_info2str(port_state->pa_info.state));
|
|
PR("\tExpired announce information "
|
|
": %s\n", port_state->pa_info.ann_expired ? "yes" : "no");
|
|
|
|
PR("PortAnnounceTransmit state machine variables:\n");
|
|
PR("\tCurrent state "
|
|
": %s\n", pa_transmit2str(port_state->pa_transmit.state));
|
|
PR("\tTrigger announce information "
|
|
": %s\n", port_state->pa_transmit.ann_trigger ? "yes" : "no");
|
|
|
|
#if defined(CONFIG_NET_GPTP_STATISTICS)
|
|
PR("\nStatistics:\n");
|
|
PR("Sync %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_sync_count);
|
|
PR("Follow Up %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_fup_count);
|
|
PR("Path Delay Request %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_pdelay_req_count);
|
|
PR("Path Delay Response %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_pdelay_resp_count);
|
|
PR("Path Delay %s threshold %s : %u\n",
|
|
"messages", "exceeded",
|
|
port_param_ds->neighbor_prop_delay_exceeded);
|
|
PR("Path Delay Follow Up %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_pdelay_resp_fup_count);
|
|
PR("Announce %s %s : %u\n",
|
|
"messages", "received", port_param_ds->rx_announce_count);
|
|
PR("ptp %s discarded : %u\n",
|
|
"messages", port_param_ds->rx_ptp_packet_discard_count);
|
|
PR("Sync %s %s : %u\n",
|
|
"reception", "timeout",
|
|
port_param_ds->sync_receipt_timeout_count);
|
|
PR("Announce %s %s : %u\n",
|
|
"reception", "timeout",
|
|
port_param_ds->announce_receipt_timeout_count);
|
|
PR("Path Delay Requests without a response "
|
|
": %u\n",
|
|
port_param_ds->pdelay_allowed_lost_resp_exceed_count);
|
|
PR("Sync %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_sync_count);
|
|
PR("Follow Up %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_fup_count);
|
|
PR("Path Delay Request %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_pdelay_req_count);
|
|
PR("Path Delay Response %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_pdelay_resp_count);
|
|
PR("Path Delay Response FUP %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_pdelay_resp_fup_count);
|
|
PR("Announce %s %s : %u\n",
|
|
"messages", "sent", port_param_ds->tx_announce_count);
|
|
#endif /* CONFIG_NET_GPTP_STATISTICS */
|
|
}
|
|
#endif /* CONFIG_NET_GPTP */
|
|
|
|
static int cmd_net_gptp_port(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_GPTP)
|
|
int arg = 1;
|
|
char *endptr;
|
|
int port;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_GPTP)
|
|
if (!argv[arg]) {
|
|
PR_WARNING("Port number must be given.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
port = strtol(argv[arg], &endptr, 10);
|
|
|
|
if (*endptr == '\0') {
|
|
gptp_print_port_info(shell, port);
|
|
} else {
|
|
PR_WARNING("Not a valid gPTP port number: %s\n", argv[arg]);
|
|
}
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_GPTP", "gPTP");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_gptp(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_GPTP)
|
|
/* gPTP status */
|
|
struct gptp_domain *domain = gptp_get_domain();
|
|
int count = 0;
|
|
int arg = 1;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_GPTP)
|
|
if (argv[arg]) {
|
|
cmd_net_gptp_port(shell, argc, argv);
|
|
} else {
|
|
struct net_shell_user_data user_data;
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
gptp_foreach_port(gptp_port_cb, &user_data);
|
|
|
|
PR("\n");
|
|
|
|
PR("SiteSyncSync state machine variables:\n");
|
|
PR("\tCurrent state : %s\n",
|
|
site_sync2str(domain->state.site_ss.state));
|
|
PR("\tA PortSyncSync struct is ready : %s\n",
|
|
domain->state.site_ss.rcvd_pss ? "yes" : "no");
|
|
|
|
PR("ClockSlaveSync state machine variables:\n");
|
|
PR("\tCurrent state : %s\n",
|
|
clk_slave2str(domain->state.clk_slave_sync.state));
|
|
PR("\tA PortSyncSync struct is ready : %s\n",
|
|
domain->state.clk_slave_sync.rcvd_pss ? "yes" : "no");
|
|
PR("\tThe local clock has expired : %s\n",
|
|
domain->state.clk_slave_sync.rcvd_local_clk_tick ?
|
|
"yes" : "no");
|
|
|
|
PR("PortRoleSelection state machine variables:\n");
|
|
PR("\tCurrent state : %s\n",
|
|
pr_selection2str(domain->state.pr_sel.state));
|
|
|
|
PR("ClockMasterSyncReceive state machine variables:\n");
|
|
PR("\tCurrent state : %s\n",
|
|
cms_rcv2str(domain->state.clk_master_sync_receive.state));
|
|
PR("\tA ClockSourceTime : %s\n",
|
|
domain->state.clk_master_sync_receive.rcvd_clock_source_req
|
|
? "yes" : "no");
|
|
PR("\tThe local clock has expired : %s\n",
|
|
domain->state.clk_master_sync_receive.rcvd_local_clock_tick
|
|
? "yes" : "no");
|
|
}
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_GPTP", "gPTP");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_iface_idx(const struct shell *shell, char *index_str)
|
|
{
|
|
char *endptr;
|
|
int idx;
|
|
|
|
if (!index_str) {
|
|
PR_WARNING("Interface index is missing.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
idx = strtol(index_str, &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid index %s\n", index_str);
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (idx < 0 || idx > 255) {
|
|
PR_WARNING("Invalid index %d\n", idx);
|
|
return -ERANGE;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static int cmd_net_iface_up(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
struct net_if *iface;
|
|
int idx, ret;
|
|
|
|
idx = get_iface_idx(shell, 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_if_is_up(iface)) {
|
|
PR_WARNING("Interface %d is already up.\n", idx);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_if_up(iface);
|
|
if (ret) {
|
|
PR_WARNING("Cannot take interface %d up (%d)\n", idx, ret);
|
|
return -ENOEXEC;
|
|
} else {
|
|
PR("Interface %d is up\n", idx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_iface_down(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
struct net_if *iface;
|
|
int idx, ret;
|
|
|
|
idx = get_iface_idx(shell, 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_if_down(iface);
|
|
if (ret) {
|
|
PR_WARNING("Cannot take interface %d down (%d)\n", idx, ret);
|
|
return -ENOEXEC;
|
|
} else {
|
|
PR("Interface %d is down\n", idx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_NATIVE_IPV6)
|
|
|
|
static void address_lifetime_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6;
|
|
const char *extra;
|
|
int i;
|
|
|
|
ARG_UNUSED(user_data);
|
|
|
|
PR("\nIPv6 addresses for interface %d (%p) (%s)\n",
|
|
net_if_get_by_iface(iface), iface, iface2str(iface, &extra));
|
|
PR("============================================%s\n", extra);
|
|
|
|
if (!ipv6) {
|
|
PR("No IPv6 config found for this interface.\n");
|
|
return;
|
|
}
|
|
|
|
PR("Type \tState \tLifetime (sec)\tAddress\n");
|
|
|
|
for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) {
|
|
struct net_if_ipv6_prefix *prefix;
|
|
char remaining_str[sizeof("01234567890")];
|
|
uint64_t remaining;
|
|
uint8_t prefix_len;
|
|
|
|
if (!ipv6->unicast[i].is_used ||
|
|
ipv6->unicast[i].address.family != AF_INET6) {
|
|
continue;
|
|
}
|
|
|
|
remaining = net_timeout_remaining(&ipv6->unicast[i].lifetime,
|
|
k_uptime_get_32());
|
|
|
|
prefix = net_if_ipv6_prefix_get(iface,
|
|
&ipv6->unicast[i].address.in6_addr);
|
|
if (prefix) {
|
|
prefix_len = prefix->len;
|
|
} else {
|
|
prefix_len = 128U;
|
|
}
|
|
|
|
if (ipv6->unicast[i].is_infinite) {
|
|
snprintk(remaining_str, sizeof(remaining_str) - 1,
|
|
"infinite");
|
|
} else {
|
|
snprintk(remaining_str, sizeof(remaining_str) - 1,
|
|
"%u", (uint32_t)(remaining / 1000U));
|
|
}
|
|
|
|
PR("%s \t%s\t%s \t%s/%d\n",
|
|
addrtype2str(ipv6->unicast[i].addr_type),
|
|
addrstate2str(ipv6->unicast[i].addr_state),
|
|
remaining_str,
|
|
net_sprint_ipv6_addr(
|
|
&ipv6->unicast[i].address.in6_addr),
|
|
prefix_len);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_NATIVE_IPV6 */
|
|
|
|
static int cmd_net_ipv6(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_NATIVE_IPV6)
|
|
struct net_shell_user_data user_data;
|
|
#endif
|
|
|
|
PR("IPv6 support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6) ?
|
|
"enabled" : "disabled");
|
|
if (!IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_NATIVE_IPV6)
|
|
PR("IPv6 fragmentation support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_FRAGMENT) ? "enabled" :
|
|
"disabled");
|
|
PR("Multicast Listener Discovery support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_MLD) ? "enabled" :
|
|
"disabled");
|
|
PR("Neighbor cache support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_NBR_CACHE) ? "enabled" :
|
|
"disabled");
|
|
PR("Neighbor discovery support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_ND) ? "enabled" :
|
|
"disabled");
|
|
PR("Duplicate address detection (DAD) support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_DAD) ? "enabled" :
|
|
"disabled");
|
|
PR("Router advertisement RDNSS option support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_IPV6_RA_RDNSS) ? "enabled" :
|
|
"disabled");
|
|
PR("6lo header compression support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_6LO) ? "enabled" :
|
|
"disabled");
|
|
|
|
if (IS_ENABLED(CONFIG_NET_6LO_CONTEXT)) {
|
|
PR("6lo context based compression "
|
|
"support : %s\n",
|
|
IS_ENABLED(CONFIG_NET_6LO_CONTEXT) ? "enabled" :
|
|
"disabled");
|
|
}
|
|
|
|
PR("Max number of IPv6 network interfaces "
|
|
"in the system : %d\n",
|
|
CONFIG_NET_IF_MAX_IPV6_COUNT);
|
|
PR("Max number of unicast IPv6 addresses "
|
|
"per network interface : %d\n",
|
|
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT);
|
|
PR("Max number of multicast IPv6 addresses "
|
|
"per network interface : %d\n",
|
|
CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT);
|
|
PR("Max number of IPv6 prefixes per network "
|
|
"interface : %d\n",
|
|
CONFIG_NET_IF_IPV6_PREFIX_COUNT);
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = NULL;
|
|
|
|
/* Print information about address lifetime */
|
|
net_if_foreach(address_lifetime_cb, &user_data);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_iface(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
struct net_if *iface = NULL;
|
|
struct net_shell_user_data user_data;
|
|
int idx;
|
|
|
|
if (argv[1]) {
|
|
idx = get_iface_idx(shell, 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 defined(CONFIG_NET_HOSTNAME_ENABLE)
|
|
PR("Hostname: %s\n\n", net_hostname_get());
|
|
#endif
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = iface;
|
|
|
|
net_if_foreach(iface_cb, &user_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct ctx_info {
|
|
int pos;
|
|
bool are_external_pools;
|
|
struct k_mem_slab *tx_slabs[CONFIG_NET_MAX_CONTEXTS];
|
|
struct net_buf_pool *data_pools[CONFIG_NET_MAX_CONTEXTS];
|
|
};
|
|
|
|
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
|
|
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
static bool slab_pool_found_already(struct ctx_info *info,
|
|
struct k_mem_slab *slab,
|
|
struct net_buf_pool *pool)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CONFIG_NET_MAX_CONTEXTS; i++) {
|
|
if (slab) {
|
|
if (info->tx_slabs[i] == slab) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (info->data_pools[i] == pool) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static void context_info(struct net_context *context, void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct ctx_info *info = data->user_data;
|
|
struct k_mem_slab *slab;
|
|
struct net_buf_pool *pool;
|
|
|
|
if (!net_context_is_used(context)) {
|
|
return;
|
|
}
|
|
|
|
if (context->tx_slab) {
|
|
slab = context->tx_slab();
|
|
|
|
if (slab_pool_found_already(info, slab, NULL)) {
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_BUF_POOL_USAGE)
|
|
PR("%p\t%u\t%u\tETX\n",
|
|
slab, slab->num_blocks, k_mem_slab_num_free_get(slab));
|
|
#else
|
|
PR("%p\t%d\tETX\n", slab, slab->num_blocks);
|
|
#endif
|
|
info->are_external_pools = true;
|
|
info->tx_slabs[info->pos] = slab;
|
|
}
|
|
|
|
if (context->data_pool) {
|
|
pool = context->data_pool();
|
|
|
|
if (slab_pool_found_already(info, NULL, pool)) {
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_BUF_POOL_USAGE)
|
|
PR("%p\t%d\t%ld\tEDATA (%s)\n", pool, pool->buf_count,
|
|
atomic_get(&pool->avail_count), pool->name);
|
|
#else
|
|
PR("%p\t%d\tEDATA\n", pool, pool->buf_count);
|
|
#endif
|
|
info->are_external_pools = true;
|
|
info->data_pools[info->pos] = pool;
|
|
}
|
|
|
|
info->pos++;
|
|
#endif /* CONFIG_NET_CONTEXT_NET_PKT_POOL */
|
|
}
|
|
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
|
|
|
|
static int cmd_net_mem(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_OFFLOAD) || defined(CONFIG_NET_NATIVE)
|
|
struct k_mem_slab *rx, *tx;
|
|
struct net_buf_pool *rx_data, *tx_data;
|
|
|
|
net_pkt_get_info(&rx, &tx, &rx_data, &tx_data);
|
|
|
|
PR("Fragment length %d bytes\n", CONFIG_NET_BUF_DATA_SIZE);
|
|
|
|
PR("Network buffer pools:\n");
|
|
|
|
#if defined(CONFIG_NET_BUF_POOL_USAGE)
|
|
PR("Address\t\tTotal\tAvail\tName\n");
|
|
|
|
PR("%p\t%d\t%u\tRX\n",
|
|
rx, rx->num_blocks, k_mem_slab_num_free_get(rx));
|
|
|
|
PR("%p\t%d\t%u\tTX\n",
|
|
tx, tx->num_blocks, k_mem_slab_num_free_get(tx));
|
|
|
|
PR("%p\t%d\t%ld\tRX DATA (%s)\n ", rx_data, rx_data->buf_count,
|
|
atomic_get(&rx_data->avail_count), rx_data->name);
|
|
|
|
PR("%p\t%d\t%ld\tTX DATA (%s)\n", tx_data, tx_data->buf_count,
|
|
atomic_get(&tx_data->avail_count), tx_data->name);
|
|
#else
|
|
PR("Address\t\tTotal\tName\n");
|
|
|
|
PR("%p\t%d\tRX\n", rx, rx->num_blocks);
|
|
PR("%p\t%d\tTX\n", tx, tx->num_blocks);
|
|
PR("%p\t%d\tRX DATA\n", rx_data, rx_data->buf_count);
|
|
PR("%p\t%d\tTX DATA\n", tx_data, tx_data->buf_count);
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_BUF_POOL_USAGE", "net_buf allocation");
|
|
#endif /* CONFIG_NET_BUF_POOL_USAGE */
|
|
|
|
if (IS_ENABLED(CONFIG_NET_CONTEXT_NET_PKT_POOL)) {
|
|
struct net_shell_user_data user_data;
|
|
struct ctx_info info;
|
|
|
|
(void)memset(&info, 0, sizeof(info));
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &info;
|
|
|
|
net_context_foreach(context_info, &user_data);
|
|
|
|
if (!info.are_external_pools) {
|
|
PR("No external memory pools found.\n");
|
|
}
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_OFFLOAD or CONFIG_NET_NATIVE", "memory usage");
|
|
#endif /* CONFIG_NET_OFFLOAD || CONFIG_NET_NATIVE */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_nbr_rm(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
struct in6_addr addr;
|
|
int ret;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
if (!argv[1]) {
|
|
PR_WARNING("Neighbor IPv6 address missing.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_addr_pton(AF_INET6, argv[1], &addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot parse '%s'\n", argv[1]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (!net_ipv6_nbr_rm(NULL, &addr)) {
|
|
PR_WARNING("Cannot remove neighbor %s\n",
|
|
net_sprint_ipv6_addr(&addr));
|
|
return -ENOEXEC;
|
|
} else {
|
|
PR("Neighbor %s removed.\n", net_sprint_ipv6_addr(&addr));
|
|
}
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("IPv6 not enabled.\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
static void nbr_cb(struct net_nbr *nbr, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
char *padding = "";
|
|
char *state_pad = "";
|
|
const char *state_str;
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
int64_t remaining;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_L2_IEEE802154)
|
|
padding = " ";
|
|
#endif
|
|
|
|
if (*count == 0) {
|
|
PR(" Neighbor Interface Flags State "
|
|
"Remain Link %sAddress\n", padding);
|
|
}
|
|
|
|
(*count)++;
|
|
|
|
state_str = net_ipv6_nbr_state2str(net_ipv6_nbr_data(nbr)->state);
|
|
|
|
/* This is not a proper way but the minimal libc does not honor
|
|
* string lengths in %s modifier so in order the output to look
|
|
* nice, do it like this.
|
|
*/
|
|
if (strlen(state_str) == 5) {
|
|
state_pad = " ";
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
remaining = net_ipv6_nbr_data(nbr)->reachable +
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout -
|
|
k_uptime_get();
|
|
#endif
|
|
|
|
PR("[%2d] %p %d %5d/%d/%d/%d %s%s %6d %17s%s %s\n",
|
|
*count, nbr, net_if_get_by_iface(nbr->iface),
|
|
net_ipv6_nbr_data(nbr)->link_metric,
|
|
nbr->ref,
|
|
net_ipv6_nbr_data(nbr)->ns_count,
|
|
net_ipv6_nbr_data(nbr)->is_router,
|
|
state_str,
|
|
state_pad,
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
(int)(remaining > 0 ? remaining : 0),
|
|
#else
|
|
0,
|
|
#endif
|
|
nbr->idx == NET_NBR_LLADDR_UNKNOWN ? "?" :
|
|
net_sprint_ll_addr(
|
|
net_nbr_get_lladdr(nbr->idx)->addr,
|
|
net_nbr_get_lladdr(nbr->idx)->len),
|
|
nbr->idx == NET_NBR_LLADDR_UNKNOWN ? "" :
|
|
(net_nbr_get_lladdr(nbr->idx)->len == 8U ? "" : padding),
|
|
net_sprint_ipv6_addr(&net_ipv6_nbr_data(nbr)->addr));
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_nbr(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
int count = 0;
|
|
struct net_shell_user_data user_data;
|
|
#endif
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
net_ipv6_nbr_foreach(nbr_cb, &user_data);
|
|
|
|
if (count == 0) {
|
|
PR("No neighbors.\n");
|
|
}
|
|
#else
|
|
PR_INFO("IPv6 not enabled.\n");
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
|
|
|
|
K_SEM_DEFINE(ping_timeout, 0, 1);
|
|
static const struct shell *shell_for_ping;
|
|
|
|
#if defined(CONFIG_NET_NATIVE_IPV6)
|
|
|
|
static enum net_verdict handle_ipv6_echo_reply(struct net_pkt *pkt,
|
|
struct net_ipv6_hdr *ip_hdr,
|
|
struct net_icmp_hdr *icmp_hdr);
|
|
|
|
static struct net_icmpv6_handler ping6_handler = {
|
|
.type = NET_ICMPV6_ECHO_REPLY,
|
|
.code = 0,
|
|
.handler = handle_ipv6_echo_reply,
|
|
};
|
|
|
|
static inline void remove_ipv6_ping_handler(void)
|
|
{
|
|
net_icmpv6_unregister_handler(&ping6_handler);
|
|
}
|
|
|
|
static enum net_verdict handle_ipv6_echo_reply(struct net_pkt *pkt,
|
|
struct net_ipv6_hdr *ip_hdr,
|
|
struct net_icmp_hdr *icmp_hdr)
|
|
{
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access,
|
|
struct net_icmpv6_echo_req);
|
|
struct net_icmpv6_echo_req *icmp_echo;
|
|
uint32_t cycles;
|
|
|
|
icmp_echo = (struct net_icmpv6_echo_req *)net_pkt_get_data(pkt,
|
|
&icmp_access);
|
|
if (icmp_echo == NULL) {
|
|
return -NET_DROP;
|
|
}
|
|
|
|
net_pkt_skip(pkt, sizeof(*icmp_echo));
|
|
if (net_pkt_read_be32(pkt, &cycles)) {
|
|
return -NET_DROP;
|
|
}
|
|
|
|
cycles = k_cycle_get_32() - cycles;
|
|
|
|
PR_SHELL(shell_for_ping, "%d bytes from %s to %s: icmp_seq=%d ttl=%d "
|
|
#ifdef CONFIG_IEEE802154
|
|
"rssi=%d "
|
|
#endif
|
|
#ifdef CONFIG_FPU
|
|
"time=%.2f ms\n",
|
|
#else
|
|
"time=%d ms\n",
|
|
#endif
|
|
ntohs(ip_hdr->len) - net_pkt_ipv6_ext_len(pkt) -
|
|
NET_ICMPH_LEN,
|
|
net_sprint_ipv6_addr(&ip_hdr->src),
|
|
net_sprint_ipv6_addr(&ip_hdr->dst),
|
|
ntohs(icmp_echo->sequence),
|
|
ip_hdr->hop_limit,
|
|
#ifdef CONFIG_IEEE802154
|
|
net_pkt_ieee802154_rssi(pkt),
|
|
#endif
|
|
#ifdef CONFIG_FPU
|
|
((uint32_t)k_cyc_to_ns_floor64(cycles) / 1000000.f));
|
|
#else
|
|
((uint32_t)k_cyc_to_ns_floor64(cycles) / 1000000));
|
|
#endif
|
|
k_sem_give(&ping_timeout);
|
|
|
|
net_pkt_unref(pkt);
|
|
return NET_OK;
|
|
}
|
|
|
|
static int ping_ipv6(const struct shell *shell,
|
|
char *host,
|
|
unsigned int count,
|
|
unsigned int interval,
|
|
int iface_idx)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(iface_idx);
|
|
int ret = 0;
|
|
struct in6_addr ipv6_target;
|
|
struct net_nbr *nbr;
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
struct net_route_entry *route;
|
|
#endif
|
|
|
|
if (net_addr_pton(AF_INET6, host, &ipv6_target) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_icmpv6_register_handler(&ping6_handler);
|
|
|
|
if (!iface) {
|
|
iface = net_if_ipv6_select_src_iface(&ipv6_target);
|
|
if (!iface) {
|
|
nbr = net_ipv6_nbr_lookup(NULL, &ipv6_target);
|
|
if (nbr) {
|
|
iface = nbr->iface;
|
|
} else {
|
|
iface = net_if_get_default();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
route = net_route_lookup(NULL, &ipv6_target);
|
|
if (route) {
|
|
iface = route->iface;
|
|
}
|
|
#endif
|
|
|
|
PR("PING %s\n", host);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
uint32_t time_stamp = htonl(k_cycle_get_32());
|
|
|
|
ret = net_icmpv6_send_echo_request(iface,
|
|
&ipv6_target,
|
|
sys_rand32_get(),
|
|
i,
|
|
&time_stamp,
|
|
sizeof(time_stamp));
|
|
if (ret) {
|
|
break;
|
|
}
|
|
|
|
k_msleep(interval);
|
|
}
|
|
|
|
remove_ipv6_ping_handler();
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define ping_ipv6(...) -ENOTSUP
|
|
#define remove_ipv6_ping_handler()
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_NATIVE_IPV4)
|
|
|
|
static enum net_verdict handle_ipv4_echo_reply(struct net_pkt *pkt,
|
|
struct net_ipv4_hdr *ip_hdr,
|
|
struct net_icmp_hdr *icmp_hdr);
|
|
|
|
static struct net_icmpv4_handler ping4_handler = {
|
|
.type = NET_ICMPV4_ECHO_REPLY,
|
|
.code = 0,
|
|
.handler = handle_ipv4_echo_reply,
|
|
};
|
|
|
|
static inline void remove_ipv4_ping_handler(void)
|
|
{
|
|
net_icmpv4_unregister_handler(&ping4_handler);
|
|
}
|
|
|
|
static enum net_verdict handle_ipv4_echo_reply(struct net_pkt *pkt,
|
|
struct net_ipv4_hdr *ip_hdr,
|
|
struct net_icmp_hdr *icmp_hdr)
|
|
{
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(icmp_access,
|
|
struct net_icmpv4_echo_req);
|
|
uint32_t cycles;
|
|
struct net_icmpv4_echo_req *icmp_echo;
|
|
|
|
icmp_echo = (struct net_icmpv4_echo_req *)net_pkt_get_data(pkt,
|
|
&icmp_access);
|
|
if (icmp_echo == NULL) {
|
|
return -NET_DROP;
|
|
}
|
|
|
|
net_pkt_skip(pkt, sizeof(*icmp_echo));
|
|
if (net_pkt_read_be32(pkt, &cycles)) {
|
|
return -NET_DROP;
|
|
}
|
|
|
|
cycles = k_cycle_get_32() - cycles;
|
|
|
|
PR_SHELL(shell_for_ping, "%d bytes from %s to %s: icmp_seq=%d ttl=%d "
|
|
#ifdef CONFIG_FPU
|
|
"time=%.2f ms\n",
|
|
#else
|
|
"time=%d ms\n",
|
|
#endif
|
|
ntohs(ip_hdr->len) - net_pkt_ipv6_ext_len(pkt) -
|
|
NET_ICMPH_LEN,
|
|
net_sprint_ipv4_addr(&ip_hdr->src),
|
|
net_sprint_ipv4_addr(&ip_hdr->dst),
|
|
ntohs(icmp_echo->sequence),
|
|
ip_hdr->ttl,
|
|
#ifdef CONFIG_FPU
|
|
((uint32_t)k_cyc_to_ns_floor64(cycles) / 1000000.f));
|
|
#else
|
|
((uint32_t)k_cyc_to_ns_floor64(cycles) / 1000000));
|
|
#endif
|
|
k_sem_give(&ping_timeout);
|
|
|
|
net_pkt_unref(pkt);
|
|
return NET_OK;
|
|
}
|
|
|
|
static int ping_ipv4(const struct shell *shell,
|
|
char *host,
|
|
unsigned int count,
|
|
unsigned int interval,
|
|
int iface_idx)
|
|
{
|
|
struct in_addr ipv4_target;
|
|
struct net_if *iface = net_if_get_by_index(iface_idx);
|
|
int ret = 0;
|
|
|
|
if (net_addr_pton(AF_INET, host, &ipv4_target) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!iface) {
|
|
iface = net_if_ipv4_select_src_iface(&ipv4_target);
|
|
}
|
|
|
|
net_icmpv4_register_handler(&ping4_handler);
|
|
|
|
PR("PING %s\n", host);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
uint32_t time_stamp = htonl(k_cycle_get_32());
|
|
|
|
ret = net_icmpv4_send_echo_request(iface,
|
|
&ipv4_target,
|
|
sys_rand32_get(),
|
|
i,
|
|
&time_stamp,
|
|
sizeof(time_stamp));
|
|
if (ret) {
|
|
break;
|
|
}
|
|
|
|
k_msleep(interval);
|
|
}
|
|
|
|
remove_ipv4_ping_handler();
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define ping_ipv4(...) -ENOTSUP
|
|
#define remove_ipv4_ping_handler()
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
static int parse_arg(size_t *i, size_t argc, char *argv[])
|
|
{
|
|
int res = -1;
|
|
const char *str = argv[*i] + 2;
|
|
char *endptr;
|
|
|
|
if (*str == 0) {
|
|
if (*i + 1 >= argc) {
|
|
return -1;
|
|
}
|
|
|
|
*i += 1;
|
|
str = argv[*i];
|
|
}
|
|
|
|
errno = 0;
|
|
res = strtol(str, &endptr, 10);
|
|
|
|
if (errno || (endptr == str)) {
|
|
return -1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */
|
|
|
|
static int cmd_net_ping(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if !defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return -EOPNOTSUPP;
|
|
#else
|
|
char *host = NULL;
|
|
int ret;
|
|
|
|
int count = 3;
|
|
int interval = 1000;
|
|
int iface_idx = -1;
|
|
|
|
for (size_t i = 1; i < argc; ++i) {
|
|
|
|
if (*argv[i] != '-') {
|
|
host = argv[i];
|
|
continue;
|
|
}
|
|
|
|
switch (argv[i][1]) {
|
|
case 'c':
|
|
count = parse_arg(&i, argc, argv);
|
|
if (count < 0) {
|
|
PR_WARNING("Parse error: %s\n", argv[i]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
|
|
break;
|
|
case 'i':
|
|
interval = parse_arg(&i, argc, argv);
|
|
if (interval < 0) {
|
|
PR_WARNING("Parse error: %s\n", argv[i]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
iface_idx = parse_arg(&i, argc, argv);
|
|
if (iface_idx < 0 || !net_if_get_by_index(iface_idx)) {
|
|
PR_WARNING("Parse error: %s\n", argv[i]);
|
|
return -ENOEXEC;
|
|
}
|
|
break;
|
|
default:
|
|
PR_WARNING("Unrecognized argument: %s\n", argv[i]);
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
if (!host) {
|
|
PR_WARNING("Target host missing\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
shell_for_ping = shell;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
ret = ping_ipv6(shell, host, count, interval, iface_idx);
|
|
if (!ret) {
|
|
goto wait_reply;
|
|
} else if (ret == -EIO) {
|
|
PR_WARNING("Cannot send IPv6 ping\n");
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
ret = ping_ipv4(shell, host, count, interval, iface_idx);
|
|
if (ret) {
|
|
if (ret == -EIO || ret == -ENETUNREACH) {
|
|
PR_WARNING("Cannot send IPv4 ping\n");
|
|
} else if (ret == -EINVAL) {
|
|
PR_WARNING("Invalid IP address\n");
|
|
} else if (ret == -ENOTSUP) {
|
|
PR_WARNING("Feature is not supported\n");
|
|
}
|
|
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
wait_reply:
|
|
ret = k_sem_take(&ping_timeout, K_SECONDS(2));
|
|
if (ret == -EAGAIN) {
|
|
PR_INFO("Ping timeout\n");
|
|
remove_ipv6_ping_handler();
|
|
remove_ipv4_ping_handler();
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static struct net_pkt *get_net_pkt(const char *ptr_str)
|
|
{
|
|
uint8_t buf[sizeof(intptr_t)];
|
|
intptr_t ptr = 0;
|
|
size_t len;
|
|
int i;
|
|
|
|
if (ptr_str[0] == '0' && ptr_str[1] == 'x') {
|
|
ptr_str += 2;
|
|
}
|
|
|
|
len = hex2bin(ptr_str, strlen(ptr_str), buf, sizeof(buf));
|
|
if (!len) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = len - 1; i >= 0; i--) {
|
|
ptr |= buf[i] << 8 * (len - 1 - i);
|
|
}
|
|
|
|
return (struct net_pkt *)ptr;
|
|
}
|
|
|
|
static void net_pkt_buffer_info(const struct shell *shell, struct net_pkt *pkt)
|
|
{
|
|
struct net_buf *buf = pkt->buffer;
|
|
|
|
PR("net_pkt %p buffer chain:\n", pkt);
|
|
PR("%p[%ld]", pkt, atomic_get(&pkt->atomic_ref));
|
|
|
|
if (buf) {
|
|
PR("->");
|
|
}
|
|
|
|
while (buf) {
|
|
PR("%p[%ld/%u (%u/%u)]", buf, atomic_get(&pkt->atomic_ref),
|
|
buf->len, net_buf_max_len(buf), buf->size);
|
|
|
|
buf = buf->frags;
|
|
if (buf) {
|
|
PR("->");
|
|
}
|
|
}
|
|
|
|
PR("\n");
|
|
}
|
|
|
|
static void net_pkt_buffer_hexdump(const struct shell *shell,
|
|
struct net_pkt *pkt)
|
|
{
|
|
struct net_buf *buf = pkt->buffer;
|
|
int i = 0;
|
|
|
|
if (!buf || buf->ref == 0) {
|
|
return;
|
|
}
|
|
|
|
PR("net_pkt %p buffer chain hexdump:\n", pkt);
|
|
|
|
while (buf) {
|
|
PR("net_buf[%d] %p\n", i++, buf);
|
|
shell_hexdump(shell, buf->data, buf->len);
|
|
buf = buf->frags;
|
|
}
|
|
}
|
|
|
|
static int cmd_net_pkt(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
if (argv[1]) {
|
|
struct net_pkt *pkt;
|
|
|
|
pkt = get_net_pkt(argv[1]);
|
|
if (!pkt) {
|
|
PR_ERROR("Invalid ptr value (%s). "
|
|
"Example: 0x01020304\n", argv[1]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
net_pkt_buffer_info(shell, pkt);
|
|
PR("\n");
|
|
net_pkt_buffer_hexdump(shell, pkt);
|
|
} else {
|
|
PR_INFO("Pointer value must be given.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_ppp_ping(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_PPP)
|
|
if (argv[1]) {
|
|
int ret, idx = get_iface_idx(shell, argv[1]);
|
|
|
|
if (idx < 0) {
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_ppp_ping(idx, MSEC_PER_SEC * 1);
|
|
if (ret < 0) {
|
|
if (ret == -EAGAIN) {
|
|
PR_INFO("PPP Echo-Req timeout.\n");
|
|
} else if (ret == -ENODEV || ret == -ENOENT) {
|
|
PR_INFO("Not a PPP interface (%d)\n", idx);
|
|
} else {
|
|
PR_INFO("PPP Echo-Req failed (%d)\n", ret);
|
|
}
|
|
} else {
|
|
if (ret > 1000) {
|
|
PR_INFO("%s%d msec\n",
|
|
"Received PPP Echo-Reply in ",
|
|
ret / 1000);
|
|
} else {
|
|
PR_INFO("%s%d usec\n",
|
|
"Received PPP Echo-Reply in ", ret);
|
|
}
|
|
}
|
|
} else {
|
|
PR_INFO("PPP network interface must be given.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_L2_PPP", "PPP");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_ppp_status(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_PPP)
|
|
int idx = 0;
|
|
struct ppp_context *ctx;
|
|
|
|
if (argv[1]) {
|
|
idx = get_iface_idx(shell, argv[1]);
|
|
if (idx < 0) {
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
ctx = net_ppp_context_get(idx);
|
|
if (!ctx) {
|
|
PR_INFO("PPP context not found.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
PR("PPP phase : %s (%d)\n", ppp_phase_str(ctx->phase),
|
|
ctx->phase);
|
|
PR("LCP state : %s (%d)\n",
|
|
ppp_state_str(ctx->lcp.fsm.state), ctx->lcp.fsm.state);
|
|
PR("LCP retransmits : %u\n", ctx->lcp.fsm.retransmits);
|
|
PR("LCP NACK loops : %u\n", ctx->lcp.fsm.nack_loops);
|
|
PR("LCP NACKs recv : %u\n", ctx->lcp.fsm.recv_nack_loops);
|
|
PR("LCP current id : %d\n", ctx->lcp.fsm.id);
|
|
PR("LCP ACK received : %s\n", ctx->lcp.fsm.ack_received ?
|
|
"yes" : "no");
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
PR("IPCP state : %s (%d)\n",
|
|
ppp_state_str(ctx->ipcp.fsm.state), ctx->ipcp.fsm.state);
|
|
PR("IPCP retransmits : %u\n", ctx->ipcp.fsm.retransmits);
|
|
PR("IPCP NACK loops : %u\n", ctx->ipcp.fsm.nack_loops);
|
|
PR("IPCP NACKs recv : %u\n", ctx->ipcp.fsm.recv_nack_loops);
|
|
PR("IPCP current id : %d\n", ctx->ipcp.fsm.id);
|
|
PR("IPCP ACK received : %s\n", ctx->ipcp.fsm.ack_received ?
|
|
"yes" : "no");
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
PR("IPv6CP state : %s (%d)\n",
|
|
ppp_state_str(ctx->ipv6cp.fsm.state), ctx->ipv6cp.fsm.state);
|
|
PR("IPv6CP retransmits : %u\n", ctx->ipv6cp.fsm.retransmits);
|
|
PR("IPv6CP NACK loops : %u\n", ctx->ipv6cp.fsm.nack_loops);
|
|
PR("IPv6CP NACKs recv : %u\n", ctx->ipv6cp.fsm.recv_nack_loops);
|
|
PR("IPv6CP current id : %d\n", ctx->ipv6cp.fsm.id);
|
|
PR("IPv6CP ACK received : %s\n", ctx->ipv6cp.fsm.ack_received ?
|
|
"yes" : "no");
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_L2_PPP_PAP)
|
|
PR("PAP state : %s (%d)\n",
|
|
ppp_state_str(ctx->pap.fsm.state), ctx->pap.fsm.state);
|
|
PR("PAP retransmits : %u\n", ctx->pap.fsm.retransmits);
|
|
PR("PAP NACK loops : %u\n", ctx->pap.fsm.nack_loops);
|
|
PR("PAP NACKs recv : %u\n", ctx->pap.fsm.recv_nack_loops);
|
|
PR("PAP current id : %d\n", ctx->pap.fsm.id);
|
|
PR("PAP ACK received : %s\n", ctx->pap.fsm.ack_received ?
|
|
"yes" : "no");
|
|
#endif /* CONFIG_NET_L2_PPP_PAP */
|
|
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_L2_PPP and CONFIG_NET_PPP", "PPP");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_route(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_NATIVE)
|
|
#if defined(CONFIG_NET_ROUTE) || defined(CONFIG_NET_ROUTE_MCAST)
|
|
struct net_shell_user_data user_data;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_ROUTE) || defined(CONFIG_NET_ROUTE_MCAST)
|
|
user_data.shell = shell;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
net_if_foreach(iface_per_route_cb, &user_data);
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_ROUTE",
|
|
"network route");
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_ROUTE_MCAST)
|
|
net_if_foreach(iface_per_mcast_route_cb, &user_data);
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_stacks(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if !defined(CONFIG_KERNEL_SHELL)
|
|
PR("Enable CONFIG_KERNEL_SHELL and type \"kernel stacks\" to see stack information.\n");
|
|
#else
|
|
PR("Type \"kernel stacks\" to see stack information.\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
|
|
static void net_shell_print_statistics_all(struct net_shell_user_data *data)
|
|
{
|
|
net_if_foreach(net_shell_print_statistics, data);
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_stats_all(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
struct net_shell_user_data user_data;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
user_data.shell = shell;
|
|
|
|
/* Print global network statistics */
|
|
net_shell_print_statistics_all(&user_data);
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
|
|
"statistics");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_stats_iface(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
#if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
|
|
struct net_shell_user_data data;
|
|
struct net_if *iface;
|
|
char *endptr;
|
|
int idx;
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
#if defined(CONFIG_NET_STATISTICS_PER_INTERFACE)
|
|
if (argv[1] == NULL) {
|
|
PR_WARNING("Network interface index missing!\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
idx = strtol(argv[1], &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid index %s\n", argv[1]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
iface = net_if_get_by_index(idx);
|
|
if (!iface) {
|
|
PR_WARNING("No such interface in index %d\n", idx);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
data.shell = shell;
|
|
|
|
net_shell_print_statistics(iface, &data);
|
|
#else
|
|
PR_INFO("Per network interface statistics not collected.\n");
|
|
PR_INFO("Please enable CONFIG_NET_STATISTICS_PER_INTERFACE\n");
|
|
#endif /* CONFIG_NET_STATISTICS_PER_INTERFACE */
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
|
|
"statistics");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_stats(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
if (!argv[1]) {
|
|
cmd_net_stats_all(shell, argc, argv);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(argv[1], "reset") == 0) {
|
|
net_stats_reset(NULL);
|
|
} else {
|
|
cmd_net_stats_iface(shell, argc, argv);
|
|
}
|
|
#else
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_STATISTICS",
|
|
"statistics");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
static struct net_context *tcp_ctx;
|
|
static const struct shell *tcp_shell;
|
|
|
|
#define TCP_CONNECT_TIMEOUT K_SECONDS(5) /* ms */
|
|
#define TCP_TIMEOUT K_SECONDS(2) /* ms */
|
|
|
|
static void tcp_connected(struct net_context *context,
|
|
int status,
|
|
void *user_data)
|
|
{
|
|
if (status < 0) {
|
|
PR_SHELL(tcp_shell, "TCP connection failed (%d)\n", status);
|
|
|
|
net_context_put(context);
|
|
|
|
tcp_ctx = NULL;
|
|
} else {
|
|
PR_SHELL(tcp_shell, "TCP connected\n");
|
|
}
|
|
}
|
|
|
|
static void get_my_ipv6_addr(struct net_if *iface,
|
|
struct sockaddr *myaddr)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
const struct in6_addr *my6addr;
|
|
|
|
my6addr = net_if_ipv6_select_src_addr(iface,
|
|
&net_sin6(myaddr)->sin6_addr);
|
|
|
|
memcpy(&net_sin6(myaddr)->sin6_addr, my6addr, sizeof(struct in6_addr));
|
|
|
|
net_sin6(myaddr)->sin6_port = 0U; /* let the IP stack to select */
|
|
#endif
|
|
}
|
|
|
|
static void get_my_ipv4_addr(struct net_if *iface,
|
|
struct sockaddr *myaddr)
|
|
{
|
|
#if defined(CONFIG_NET_NATIVE_IPV4)
|
|
/* Just take the first IPv4 address of an interface. */
|
|
memcpy(&net_sin(myaddr)->sin_addr,
|
|
&iface->config.ip.ipv4->unicast[0].address.in_addr,
|
|
sizeof(struct in_addr));
|
|
|
|
net_sin(myaddr)->sin_port = 0U; /* let the IP stack to select */
|
|
#endif
|
|
}
|
|
|
|
static void print_connect_info(const struct shell *shell,
|
|
int family,
|
|
struct sockaddr *myaddr,
|
|
struct sockaddr *addr)
|
|
{
|
|
switch (family) {
|
|
case AF_INET:
|
|
if (IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
PR("Connecting from %s:%u ",
|
|
net_sprint_ipv4_addr(&net_sin(myaddr)->sin_addr),
|
|
ntohs(net_sin(myaddr)->sin_port));
|
|
PR("to %s:%u\n",
|
|
net_sprint_ipv4_addr(&net_sin(addr)->sin_addr),
|
|
ntohs(net_sin(addr)->sin_port));
|
|
} else {
|
|
PR_INFO("IPv4 not supported\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case AF_INET6:
|
|
if (IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
PR("Connecting from [%s]:%u ",
|
|
net_sprint_ipv6_addr(&net_sin6(myaddr)->sin6_addr),
|
|
ntohs(net_sin6(myaddr)->sin6_port));
|
|
PR("to [%s]:%u\n",
|
|
net_sprint_ipv6_addr(&net_sin6(addr)->sin6_addr),
|
|
ntohs(net_sin6(addr)->sin6_port));
|
|
} else {
|
|
PR_INFO("IPv6 not supported\n");
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
PR_WARNING("Unknown protocol family (%d)\n", family);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void tcp_connect(const struct shell *shell, char *host, uint16_t port,
|
|
struct net_context **ctx)
|
|
{
|
|
struct net_if *iface = net_if_get_default();
|
|
struct sockaddr myaddr;
|
|
struct sockaddr addr;
|
|
struct net_nbr *nbr;
|
|
int addrlen;
|
|
int family;
|
|
int ret;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
ret = net_addr_pton(AF_INET6, host,
|
|
&net_sin6(&addr)->sin6_addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Invalid IPv6 address\n");
|
|
return;
|
|
}
|
|
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
|
|
nbr = net_ipv6_nbr_lookup(NULL, &net_sin6(&addr)->sin6_addr);
|
|
if (nbr) {
|
|
iface = nbr->iface;
|
|
}
|
|
|
|
get_my_ipv6_addr(iface, &myaddr);
|
|
family = addr.sa_family = myaddr.sa_family = AF_INET6;
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) &&
|
|
!IS_ENABLED(CONFIG_NET_IPV6)) {
|
|
ARG_UNUSED(nbr);
|
|
|
|
ret = net_addr_pton(AF_INET, host, &net_sin(&addr)->sin_addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Invalid IPv4 address\n");
|
|
return;
|
|
}
|
|
|
|
get_my_ipv4_addr(iface, &myaddr);
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
family = addr.sa_family = myaddr.sa_family = AF_INET;
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
|
|
IS_ENABLED(CONFIG_NET_IPV4)) {
|
|
ret = net_addr_pton(AF_INET6, host,
|
|
&net_sin6(&addr)->sin6_addr);
|
|
if (ret < 0) {
|
|
ret = net_addr_pton(AF_INET, host,
|
|
&net_sin(&addr)->sin_addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Invalid IP address\n");
|
|
return;
|
|
}
|
|
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
|
|
get_my_ipv4_addr(iface, &myaddr);
|
|
family = addr.sa_family = myaddr.sa_family = AF_INET;
|
|
} else {
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
|
|
nbr = net_ipv6_nbr_lookup(NULL,
|
|
&net_sin6(&addr)->sin6_addr);
|
|
if (nbr) {
|
|
iface = nbr->iface;
|
|
}
|
|
|
|
get_my_ipv6_addr(iface, &myaddr);
|
|
family = addr.sa_family = myaddr.sa_family = AF_INET6;
|
|
}
|
|
} else {
|
|
PR_WARNING("No IPv6 nor IPv4 is enabled\n");
|
|
return;
|
|
}
|
|
|
|
print_connect_info(shell, family, &myaddr, &addr);
|
|
|
|
ret = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot get TCP context (%d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
ret = net_context_bind(*ctx, &myaddr, addrlen);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot bind TCP (%d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
/* Note that we cannot put shell as a user_data when connecting
|
|
* because the tcp_connected() will be called much later and
|
|
* all local stack variables are lost at that point.
|
|
*/
|
|
tcp_shell = shell;
|
|
|
|
#if defined(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)
|
|
#define CONNECT_TIMEOUT K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)
|
|
#else
|
|
#define CONNECT_TIMEOUT K_SECONDS(3)
|
|
#endif
|
|
|
|
net_context_connect(*ctx, &addr, addrlen, tcp_connected,
|
|
CONNECT_TIMEOUT, NULL);
|
|
}
|
|
|
|
static void tcp_sent_cb(struct net_context *context,
|
|
int status, void *user_data)
|
|
{
|
|
PR_SHELL(tcp_shell, "Message sent\n");
|
|
}
|
|
|
|
static void tcp_recv_cb(struct net_context *context, struct net_pkt *pkt,
|
|
union net_ip_header *ip_hdr,
|
|
union net_proto_header *proto_hdr,
|
|
int status, void *user_data)
|
|
{
|
|
int ret, len;
|
|
|
|
if (pkt == NULL) {
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
return;
|
|
}
|
|
|
|
ret = net_context_put(tcp_ctx);
|
|
if (ret < 0) {
|
|
PR_SHELL(tcp_shell,
|
|
"Cannot close the connection (%d)\n", ret);
|
|
return;
|
|
}
|
|
|
|
PR_SHELL(tcp_shell, "Connection closed by remote peer.\n");
|
|
tcp_ctx = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
len = net_pkt_remaining_data(pkt);
|
|
|
|
(void)net_context_update_recv_wnd(context, len);
|
|
|
|
PR_SHELL(tcp_shell, "%zu bytes received\n", net_pkt_get_len(pkt));
|
|
|
|
net_pkt_unref(pkt);
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_tcp_connect(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
int arg = 0;
|
|
|
|
/* tcp connect <ip> port */
|
|
char *endptr;
|
|
char *ip;
|
|
uint16_t port;
|
|
|
|
/* tcp connect <ip> port */
|
|
if (tcp_ctx && net_context_is_used(tcp_ctx)) {
|
|
PR("Already connected\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("Peer IP address missing.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ip = argv[arg];
|
|
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("Peer port missing.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
port = strtol(argv[arg], &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid port %s\n", argv[arg]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
tcp_connect(shell, ip, port, &tcp_ctx);
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
|
|
#endif /* CONFIG_NET_NATIVE_TCP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_tcp_send(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
int arg = 0;
|
|
int ret;
|
|
struct net_shell_user_data user_data;
|
|
|
|
/* tcp send <data> */
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
PR_WARNING("Not connected\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("No data to send.\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
user_data.shell = shell;
|
|
|
|
ret = net_context_send(tcp_ctx, (uint8_t *)argv[arg],
|
|
strlen(argv[arg]), tcp_sent_cb,
|
|
TCP_TIMEOUT, &user_data);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot send msg (%d)\n", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
|
|
#endif /* CONFIG_NET_NATIVE_TCP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_tcp_recv(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
int ret;
|
|
struct net_shell_user_data user_data;
|
|
|
|
/* tcp recv */
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
PR_WARNING("Not connected\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
user_data.shell = shell;
|
|
|
|
ret = net_context_recv(tcp_ctx, tcp_recv_cb, K_NO_WAIT, &user_data);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot recv data (%d)\n", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
|
|
#endif /* CONFIG_NET_NATIVE_TCP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_tcp_close(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_TCP) && defined(CONFIG_NET_NATIVE_TCP)
|
|
int ret;
|
|
|
|
/* tcp close */
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
PR_WARNING("Not connected\n");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_context_put(tcp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot close the connection (%d)\n", ret);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
PR("Connection closed.\n");
|
|
tcp_ctx = NULL;
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n",
|
|
"CONFIG_NET_TCP and CONFIG_NET_NATIVE", "TCP");
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_tcp(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_UDP) && defined(CONFIG_NET_NATIVE_UDP)
|
|
static struct net_context *udp_ctx;
|
|
static const struct shell *udp_shell;
|
|
K_SEM_DEFINE(udp_send_wait, 0, 1);
|
|
|
|
static void udp_rcvd(struct net_context *context, struct net_pkt *pkt,
|
|
union net_ip_header *ip_hdr,
|
|
union net_proto_header *proto_hdr, int status,
|
|
void *user_data)
|
|
{
|
|
if (pkt) {
|
|
size_t len = net_pkt_remaining_data(pkt);
|
|
uint8_t byte;
|
|
|
|
PR_SHELL(udp_shell, "Received UDP packet: ");
|
|
for (size_t i = 0; i < len; ++i) {
|
|
net_pkt_read_u8(pkt, &byte);
|
|
PR_SHELL(udp_shell, "%02x ", byte);
|
|
}
|
|
PR_SHELL(udp_shell, "\n");
|
|
}
|
|
}
|
|
|
|
static void udp_sent(struct net_context *context, int status, void *user_data)
|
|
{
|
|
ARG_UNUSED(context);
|
|
ARG_UNUSED(status);
|
|
ARG_UNUSED(user_data);
|
|
|
|
PR_SHELL(udp_shell, "Message sent\n");
|
|
k_sem_give(&udp_send_wait);
|
|
}
|
|
#endif
|
|
|
|
static int cmd_net_udp_bind(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return -EOPNOTSUPP;
|
|
#else
|
|
char *addr_str = NULL;
|
|
char *endptr = NULL;
|
|
uint16_t port;
|
|
int ret;
|
|
|
|
struct net_if *iface;
|
|
struct sockaddr addr;
|
|
int addrlen;
|
|
|
|
if (argc < 3) {
|
|
PR_WARNING("Not enough arguments given for udp bind command\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
addr_str = argv[1];
|
|
port = strtol(argv[2], &endptr, 0);
|
|
|
|
if (endptr == argv[2]) {
|
|
PR_WARNING("Invalid port number\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (udp_ctx && net_context_is_used(udp_ctx)) {
|
|
PR_WARNING("Network context already in use\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
ret = net_ipaddr_parse(addr_str, strlen(addr_str), &addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot parse address \"%s\"\n", addr_str);
|
|
return ret;
|
|
}
|
|
|
|
ret = net_context_get(addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
|
|
&udp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot get UDP context (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
udp_shell = shell;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
|
|
iface = net_if_ipv6_select_src_iface(
|
|
&net_sin6(&addr)->sin6_addr);
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
|
|
iface = net_if_ipv4_select_src_iface(
|
|
&net_sin(&addr)->sin_addr);
|
|
} else {
|
|
PR_WARNING("IPv6 and IPv4 are disabled, cannot %s.\n", "bind");
|
|
goto release_ctx;
|
|
}
|
|
|
|
if (!iface) {
|
|
PR_WARNING("No interface to send to given host\n");
|
|
goto release_ctx;
|
|
}
|
|
|
|
net_context_set_iface(udp_ctx, iface);
|
|
|
|
ret = net_context_bind(udp_ctx, &addr, addrlen);
|
|
if (ret < 0) {
|
|
PR_WARNING("Binding to UDP port failed (%d)\n", ret);
|
|
goto release_ctx;
|
|
}
|
|
|
|
ret = net_context_recv(udp_ctx, udp_rcvd, K_NO_WAIT, NULL);
|
|
if (ret < 0) {
|
|
PR_WARNING("Receiving from UDP port failed (%d)\n", ret);
|
|
goto release_ctx;
|
|
}
|
|
|
|
return 0;
|
|
|
|
release_ctx:
|
|
ret = net_context_put(udp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot put UDP context (%d)\n", ret);
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int cmd_net_udp_close(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return -EOPNOTSUPP;
|
|
#else
|
|
int ret;
|
|
|
|
if (!udp_ctx || !net_context_is_used(udp_ctx)) {
|
|
PR_WARNING("Network context is not used. Cannot close.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = net_context_put(udp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot close UDP port (%d)\n", ret);
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int cmd_net_udp_send(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if !defined(CONFIG_NET_UDP) || !defined(CONFIG_NET_NATIVE_UDP)
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return -EOPNOTSUPP;
|
|
#else
|
|
char *host = NULL;
|
|
char *endptr = NULL;
|
|
uint16_t port;
|
|
uint8_t *payload = NULL;
|
|
int ret;
|
|
|
|
struct net_if *iface;
|
|
struct sockaddr addr;
|
|
int addrlen;
|
|
|
|
if (argc < 4) {
|
|
PR_WARNING("Not enough arguments given for udp send command\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
host = argv[1];
|
|
port = strtol(argv[2], &endptr, 0);
|
|
payload = argv[3];
|
|
|
|
if (endptr == argv[2]) {
|
|
PR_WARNING("Invalid port number\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (udp_ctx && net_context_is_used(udp_ctx)) {
|
|
PR_WARNING("Network context already in use\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
ret = net_ipaddr_parse(host, strlen(host), &addr);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot parse address \"%s\"\n", host);
|
|
return ret;
|
|
}
|
|
|
|
ret = net_context_get(addr.sa_family, SOCK_DGRAM, IPPROTO_UDP,
|
|
&udp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot get UDP context (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
udp_shell = shell;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV6) && addr.sa_family == AF_INET6) {
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
|
|
iface = net_if_ipv6_select_src_iface(
|
|
&net_sin6(&addr)->sin6_addr);
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV4) && addr.sa_family == AF_INET) {
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
|
|
iface = net_if_ipv4_select_src_iface(
|
|
&net_sin(&addr)->sin_addr);
|
|
} else {
|
|
PR_WARNING("IPv6 and IPv4 are disabled, cannot %s.\n", "send");
|
|
goto release_ctx;
|
|
}
|
|
|
|
if (!iface) {
|
|
PR_WARNING("No interface to send to given host\n");
|
|
goto release_ctx;
|
|
}
|
|
|
|
net_context_set_iface(udp_ctx, iface);
|
|
|
|
ret = net_context_recv(udp_ctx, udp_rcvd, K_NO_WAIT, NULL);
|
|
if (ret < 0) {
|
|
PR_WARNING("Setting rcv callback failed (%d)\n", ret);
|
|
goto release_ctx;
|
|
}
|
|
|
|
ret = net_context_sendto(udp_ctx, payload, strlen(payload), &addr,
|
|
addrlen, udp_sent, K_FOREVER, NULL);
|
|
if (ret < 0) {
|
|
PR_WARNING("Sending packet failed (%d)\n", ret);
|
|
goto release_ctx;
|
|
}
|
|
|
|
ret = k_sem_take(&udp_send_wait, K_SECONDS(2));
|
|
if (ret == -EAGAIN) {
|
|
PR_WARNING("UDP packet sending failed\n");
|
|
}
|
|
|
|
release_ctx:
|
|
ret = net_context_put(udp_ctx);
|
|
if (ret < 0) {
|
|
PR_WARNING("Cannot put UDP context (%d)\n", ret);
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int cmd_net_udp(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(shell);
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_L2_VIRTUAL)
|
|
static void virtual_iface_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
char *name, buf[CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN];
|
|
struct net_if *orig_iface;
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
|
|
return;
|
|
}
|
|
|
|
if (*count == 0) {
|
|
PR("Interface Attached-To Description\n");
|
|
(*count)++;
|
|
}
|
|
|
|
orig_iface = net_virtual_get_iface(iface);
|
|
|
|
name = net_virtual_get_name(iface, buf, sizeof(buf));
|
|
|
|
PR("%d %c %s\n",
|
|
net_if_get_by_iface(iface),
|
|
orig_iface ? net_if_get_by_iface(orig_iface) + '0' : '-',
|
|
name);
|
|
|
|
(*count)++;
|
|
}
|
|
|
|
static void attached_iface_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
char buf[CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN];
|
|
const char *name;
|
|
struct virtual_interface_context *ctx, *tmp;
|
|
|
|
if (sys_slist_is_empty(&iface->config.virtual_interfaces)) {
|
|
return;
|
|
}
|
|
|
|
if (*count == 0) {
|
|
PR("Interface Below-of Description\n");
|
|
(*count)++;
|
|
}
|
|
|
|
PR("%d ", net_if_get_by_iface(iface));
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&iface->config.virtual_interfaces,
|
|
ctx, tmp, node) {
|
|
if (ctx->virtual_iface == iface) {
|
|
continue;
|
|
}
|
|
|
|
PR("%d ", net_if_get_by_iface(ctx->virtual_iface));
|
|
}
|
|
|
|
name = net_virtual_get_name(iface, buf, sizeof(buf));
|
|
if (name == NULL) {
|
|
name = iface2str(iface, NULL);
|
|
}
|
|
|
|
PR(" %s\n", name);
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_L2_VIRTUAL */
|
|
|
|
static int cmd_net_virtual(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_L2_VIRTUAL)
|
|
struct net_shell_user_data user_data;
|
|
int count = 0;
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
net_if_foreach(virtual_iface_cb, &user_data);
|
|
|
|
count = 0;
|
|
PR("\n");
|
|
|
|
net_if_foreach(attached_iface_cb, &user_data);
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_L2_VIRTUAL",
|
|
"virtual network interface");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
static void iface_vlan_del_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
uint16_t vlan_tag = POINTER_TO_UINT(data->user_data);
|
|
int ret;
|
|
|
|
ret = net_eth_vlan_disable(iface, vlan_tag);
|
|
if (ret < 0) {
|
|
if (ret != -ESRCH) {
|
|
PR_WARNING("Cannot delete VLAN tag %d from "
|
|
"interface %d (%p)\n",
|
|
vlan_tag,
|
|
net_if_get_by_iface(iface),
|
|
iface);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
PR("VLAN tag %d removed from interface %d (%p)\n", vlan_tag,
|
|
net_if_get_by_iface(iface), iface);
|
|
}
|
|
|
|
static void iface_vlan_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
int *count = data->user_data;
|
|
int i;
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
return;
|
|
}
|
|
|
|
if (*count == 0) {
|
|
PR(" Interface Type Tag\n");
|
|
}
|
|
|
|
if (!ctx->vlan_enabled) {
|
|
PR_WARNING("VLAN tag(s) not set\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NET_VLAN_MAX_COUNT; i++) {
|
|
if (!ctx->vlan[i].iface || ctx->vlan[i].iface != iface) {
|
|
continue;
|
|
}
|
|
|
|
if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC) {
|
|
continue;
|
|
}
|
|
|
|
PR("[%d] %p %s %d\n", net_if_get_by_iface(iface), iface,
|
|
iface2str(iface, NULL), ctx->vlan[i].tag);
|
|
|
|
break;
|
|
}
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
static int cmd_net_vlan(const struct shell *shell, size_t argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_VLAN)
|
|
struct net_shell_user_data user_data;
|
|
int count;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
count = 0;
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
net_if_foreach(iface_vlan_cb, &user_data);
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_VLAN", "VLAN");
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_vlan_add(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_VLAN)
|
|
int arg = 0;
|
|
int ret;
|
|
uint16_t tag;
|
|
struct net_if *iface;
|
|
char *endptr;
|
|
uint32_t iface_idx;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
/* vlan add <tag> <interface index> */
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("VLAN tag missing.\n");
|
|
goto usage;
|
|
}
|
|
|
|
tag = strtol(argv[arg], &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid tag %s\n", argv[arg]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("Network interface index missing.\n");
|
|
goto usage;
|
|
}
|
|
|
|
iface_idx = strtol(argv[arg], &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid index %s\n", argv[arg]);
|
|
goto usage;
|
|
}
|
|
|
|
iface = net_if_get_by_index(iface_idx);
|
|
if (!iface) {
|
|
PR_WARNING("Network interface index %d is invalid.\n",
|
|
iface_idx);
|
|
goto usage;
|
|
}
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
PR_WARNING("Network interface %d (%p) is not ethernet interface\n",
|
|
net_if_get_by_iface(iface), iface);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
ret = net_eth_vlan_enable(iface, tag);
|
|
if (ret < 0) {
|
|
if (ret == -ENOENT) {
|
|
PR_WARNING("No IP address configured.\n");
|
|
}
|
|
|
|
PR_WARNING("Cannot set VLAN tag (%d)\n", ret);
|
|
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
PR("VLAN tag %d set to interface %d (%p)\n", tag,
|
|
net_if_get_by_iface(iface), iface);
|
|
|
|
return 0;
|
|
|
|
usage:
|
|
PR("Usage:\n");
|
|
PR("\tvlan add <tag> <interface index>\n");
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_VLAN", "VLAN");
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_vlan_del(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_VLAN)
|
|
int arg = 0;
|
|
struct net_shell_user_data user_data;
|
|
char *endptr;
|
|
uint16_t tag;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
/* vlan del <tag> */
|
|
if (!argv[++arg]) {
|
|
PR_WARNING("VLAN tag missing.\n");
|
|
goto usage;
|
|
}
|
|
|
|
tag = strtol(argv[arg], &endptr, 10);
|
|
if (*endptr != '\0') {
|
|
PR_WARNING("Invalid tag %s\n", argv[arg]);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = UINT_TO_POINTER((uint32_t)tag);
|
|
|
|
net_if_foreach(iface_vlan_del_cb, &user_data);
|
|
|
|
return 0;
|
|
|
|
usage:
|
|
PR("Usage:\n");
|
|
PR("\tvlan del <tag>\n");
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_NET_VLAN", "VLAN");
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_suspend(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_POWER_MANAGEMENT)
|
|
if (argv[1]) {
|
|
struct net_if *iface = NULL;
|
|
const struct device *dev;
|
|
int idx;
|
|
int ret;
|
|
|
|
idx = get_iface_idx(shell, 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;
|
|
}
|
|
|
|
dev = net_if_get_device(iface);
|
|
|
|
ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
|
|
if (ret != 0) {
|
|
PR_INFO("Iface could not be suspended: ");
|
|
|
|
if (ret == -EBUSY) {
|
|
PR_INFO("device is busy\n");
|
|
} else if (ret == -EALREADY) {
|
|
PR_INFO("dehive is already suspended\n");
|
|
}
|
|
}
|
|
} else {
|
|
PR("Usage:\n");
|
|
PR("\tsuspend <iface index>\n");
|
|
}
|
|
#else
|
|
PR_INFO("You need a network driver supporting Power Management.\n");
|
|
#endif /* CONFIG_NET_POWER_MANAGEMENT */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_net_resume(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_POWER_MANAGEMENT)
|
|
if (argv[1]) {
|
|
struct net_if *iface = NULL;
|
|
const struct device *dev;
|
|
int idx;
|
|
int ret;
|
|
|
|
idx = get_iface_idx(shell, 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;
|
|
}
|
|
|
|
dev = net_if_get_device(iface);
|
|
|
|
ret = pm_device_action_run(dev, PM_DEVICE_ACTION_RESUME);
|
|
if (ret != 0) {
|
|
PR_INFO("Iface could not be resumed\n");
|
|
}
|
|
|
|
} else {
|
|
PR("Usage:\n");
|
|
PR("\tresume <iface index>\n");
|
|
}
|
|
#else
|
|
PR_INFO("You need a network driver supporting Power Management.\n");
|
|
#endif /* CONFIG_NET_POWER_MANAGEMENT */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_WEBSOCKET_CLIENT)
|
|
static void websocket_context_cb(struct websocket_context *context,
|
|
void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#elif defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
|
|
#define ADDR_LEN NET_IPV4_ADDR_LEN
|
|
#else
|
|
#define ADDR_LEN NET_IPV6_ADDR_LEN
|
|
#endif
|
|
struct net_shell_user_data *data = user_data;
|
|
const struct shell *shell = data->shell;
|
|
struct net_context *net_ctx;
|
|
int *count = data->user_data;
|
|
/* +7 for []:port */
|
|
char addr_local[ADDR_LEN + 7];
|
|
char addr_remote[ADDR_LEN + 7] = "";
|
|
|
|
net_ctx = z_get_fd_obj(context->real_sock, NULL, 0);
|
|
if (net_ctx == NULL) {
|
|
PR_ERROR("Invalid fd %d", context->real_sock);
|
|
return;
|
|
}
|
|
|
|
get_addresses(net_ctx, addr_local, sizeof(addr_local),
|
|
addr_remote, sizeof(addr_remote));
|
|
|
|
PR("[%2d] %p/%p\t%p %16s\t%16s\n",
|
|
(*count) + 1, context, net_ctx,
|
|
net_context_get_iface(net_ctx),
|
|
addr_local, addr_remote);
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_WEBSOCKET_CLIENT */
|
|
|
|
static int cmd_net_websocket(const struct shell *shell, size_t argc,
|
|
char *argv[])
|
|
{
|
|
#if defined(CONFIG_WEBSOCKET_CLIENT)
|
|
struct net_shell_user_data user_data;
|
|
int count = 0;
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
PR(" websocket/net_ctx\tIface "
|
|
"Local \tRemote\n");
|
|
|
|
user_data.shell = shell;
|
|
user_data.user_data = &count;
|
|
|
|
websocket_context_foreach(websocket_context_cb, &user_data);
|
|
|
|
if (count == 0) {
|
|
PR("No connections\n");
|
|
}
|
|
#else
|
|
PR_INFO("Set %s to enable %s support.\n", "CONFIG_WEBSOCKET_CLIENT",
|
|
"Websocket");
|
|
#endif /* CONFIG_WEBSOCKET_CLIENT */
|
|
|
|
return 0;
|
|
}
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_arp,
|
|
SHELL_CMD(flush, NULL, "Remove all entries from ARP cache.",
|
|
cmd_net_arp_flush),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_capture,
|
|
SHELL_CMD(setup, NULL, "Setup network packet capture.\n"
|
|
"'net capture setup <remote-ip-addr> <local-addr> <peer-addr>'\n"
|
|
"<remote> is the (outer) endpoint IP address,\n"
|
|
"<local> is the (inner) local IP address,\n"
|
|
"<peer> is the (inner) peer IP address\n"
|
|
"Local and Peer addresses can have UDP port number in them (optional)\n"
|
|
"like 198.0.51.2:9000 or [2001:db8:100::2]:4242",
|
|
cmd_net_capture_setup),
|
|
SHELL_CMD(cleanup, NULL, "Cleanup network packet capture.",
|
|
cmd_net_capture_cleanup),
|
|
SHELL_CMD(enable, NULL, "Enable network packet capture for a given "
|
|
"network interface.\n"
|
|
"'net capture enable <interface index>'",
|
|
cmd_net_capture_enable),
|
|
SHELL_CMD(disable, NULL, "Disable network packet capture.",
|
|
cmd_net_capture_disable),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_dns,
|
|
SHELL_CMD(cancel, NULL, "Cancel all pending requests.",
|
|
cmd_net_dns_cancel),
|
|
SHELL_CMD(query, NULL,
|
|
"'net dns <hostname> [A or AAAA]' queries IPv4 address "
|
|
"(default) or IPv6 address for a host name.",
|
|
cmd_net_dns_query),
|
|
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 "
|
|
"gPTP port.",
|
|
cmd_net_gptp_port),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
#if !defined(NET_VLAN_MAX_COUNT)
|
|
#define MAX_IFACE_COUNT NET_IF_MAX_CONFIGS
|
|
#else
|
|
#define MAX_IFACE_COUNT NET_VLAN_MAX_COUNT
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_SHELL_DYN_CMD_COMPLETION)
|
|
|
|
#define MAX_IFACE_HELP_STR_LEN sizeof("longbearername (0xabcd0123)")
|
|
#define MAX_IFACE_STR_LEN sizeof("xxx")
|
|
|
|
static char iface_help_buffer[MAX_IFACE_COUNT][MAX_IFACE_HELP_STR_LEN];
|
|
static char iface_index_buffer[MAX_IFACE_COUNT][MAX_IFACE_STR_LEN];
|
|
|
|
static char *set_iface_index_buffer(size_t idx)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(idx);
|
|
|
|
if (!iface) {
|
|
return NULL;
|
|
}
|
|
|
|
snprintk(iface_index_buffer[idx], MAX_IFACE_STR_LEN, "%zu", idx);
|
|
|
|
return iface_index_buffer[idx];
|
|
}
|
|
|
|
static char *set_iface_index_help(size_t idx)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(idx);
|
|
|
|
if (!iface) {
|
|
return NULL;
|
|
}
|
|
|
|
snprintk(iface_help_buffer[idx], MAX_IFACE_HELP_STR_LEN,
|
|
"%s (%p)", iface2str(iface, NULL), iface);
|
|
|
|
return iface_help_buffer[idx];
|
|
}
|
|
|
|
static void iface_index_get(size_t idx, struct shell_static_entry *entry);
|
|
|
|
SHELL_DYNAMIC_CMD_CREATE(iface_index, iface_index_get);
|
|
|
|
static void iface_index_get(size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
entry->handler = NULL;
|
|
entry->help = set_iface_index_help(idx);
|
|
entry->subcmd = &iface_index;
|
|
entry->syntax = set_iface_index_buffer(idx);
|
|
}
|
|
|
|
#define IFACE_DYN_CMD &iface_index
|
|
|
|
#if defined(CONFIG_NET_PPP)
|
|
static char *set_iface_ppp_index_buffer(size_t idx)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(idx);
|
|
|
|
if (!iface) {
|
|
return NULL;
|
|
}
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(PPP)) {
|
|
return NULL;
|
|
}
|
|
|
|
snprintk(iface_index_buffer[idx], MAX_IFACE_STR_LEN, "%zu", idx);
|
|
|
|
return iface_index_buffer[idx];
|
|
}
|
|
|
|
static char *set_iface_ppp_index_help(size_t idx)
|
|
{
|
|
struct net_if *iface = net_if_get_by_index(idx);
|
|
|
|
if (!iface) {
|
|
return NULL;
|
|
}
|
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(PPP)) {
|
|
return NULL;
|
|
}
|
|
|
|
snprintk(iface_help_buffer[idx], MAX_IFACE_HELP_STR_LEN,
|
|
"%s (%p)", iface2str(iface, NULL), iface);
|
|
|
|
return iface_help_buffer[idx];
|
|
}
|
|
|
|
static void iface_ppp_index_get(size_t idx, struct shell_static_entry *entry);
|
|
|
|
SHELL_DYNAMIC_CMD_CREATE(iface_ppp_index, iface_ppp_index_get);
|
|
|
|
static void iface_ppp_index_get(size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
entry->handler = NULL;
|
|
entry->help = set_iface_ppp_index_help(idx);
|
|
entry->subcmd = &iface_ppp_index;
|
|
entry->syntax = set_iface_ppp_index_buffer(idx);
|
|
}
|
|
|
|
#define IFACE_PPP_DYN_CMD &iface_ppp_index
|
|
#else
|
|
#define IFACE_PPP_DYN_CMD NULL
|
|
#endif /* CONFIG_NET_PPP */
|
|
|
|
#else
|
|
#define IFACE_DYN_CMD NULL
|
|
#define IFACE_PPP_DYN_CMD NULL
|
|
#endif /* CONFIG_NET_SHELL_DYN_CMD_COMPLETION */
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_iface,
|
|
SHELL_CMD(up, IFACE_DYN_CMD,
|
|
"'net iface up <index>' takes network interface up.",
|
|
cmd_net_iface_up),
|
|
SHELL_CMD(down, IFACE_DYN_CMD,
|
|
"'net iface down <index>' takes network interface "
|
|
"down.",
|
|
cmd_net_iface_down),
|
|
SHELL_CMD(show, IFACE_DYN_CMD,
|
|
"'net iface <index>' shows network interface "
|
|
"information.",
|
|
cmd_net_iface),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_ppp,
|
|
SHELL_CMD(ping, IFACE_PPP_DYN_CMD,
|
|
"'net ppp ping <index>' sends Echo-request to PPP interface.",
|
|
cmd_net_ppp_ping),
|
|
SHELL_CMD(status, NULL,
|
|
"'net ppp status' prints information about PPP.",
|
|
cmd_net_ppp_status),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_SHELL_DYN_CMD_COMPLETION)
|
|
static
|
|
char nbr_address_buffer[CONFIG_NET_IPV6_MAX_NEIGHBORS][NET_IPV6_ADDR_LEN];
|
|
|
|
static void nbr_address_cb(struct net_nbr *nbr, void *user_data)
|
|
{
|
|
int *count = user_data;
|
|
|
|
if (*count >= CONFIG_NET_IPV6_MAX_NEIGHBORS) {
|
|
return;
|
|
}
|
|
|
|
snprintk(nbr_address_buffer[*count], NET_IPV6_ADDR_LEN,
|
|
"%s", net_sprint_ipv6_addr(&net_ipv6_nbr_data(nbr)->addr));
|
|
|
|
(*count)++;
|
|
}
|
|
|
|
static void nbr_populate_addresses(void)
|
|
{
|
|
int count = 0;
|
|
|
|
net_ipv6_nbr_foreach(nbr_address_cb, &count);
|
|
}
|
|
|
|
static char *set_nbr_address(size_t idx)
|
|
{
|
|
if (idx == 0) {
|
|
memset(nbr_address_buffer, 0, sizeof(nbr_address_buffer));
|
|
nbr_populate_addresses();
|
|
}
|
|
|
|
if (idx >= CONFIG_NET_IPV6_MAX_NEIGHBORS) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!nbr_address_buffer[idx][0]) {
|
|
return NULL;
|
|
}
|
|
|
|
return nbr_address_buffer[idx];
|
|
}
|
|
|
|
static void nbr_address_get(size_t idx, struct shell_static_entry *entry);
|
|
|
|
SHELL_DYNAMIC_CMD_CREATE(nbr_address, nbr_address_get);
|
|
|
|
#define NBR_ADDRESS_CMD &nbr_address
|
|
|
|
static void nbr_address_get(size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
entry->handler = NULL;
|
|
entry->help = NULL;
|
|
entry->subcmd = &nbr_address;
|
|
entry->syntax = set_nbr_address(idx);
|
|
}
|
|
|
|
#else
|
|
#define NBR_ADDRESS_CMD NULL
|
|
#endif /* CONFIG_NET_IPV6 && CONFIG_NET_SHELL_DYN_CMD_COMPLETION */
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_nbr,
|
|
SHELL_CMD(rm, NBR_ADDRESS_CMD,
|
|
"'net nbr rm <address>' removes neighbor from cache.",
|
|
cmd_net_nbr_rm),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
#if defined(CONFIG_NET_STATISTICS) && \
|
|
defined(CONFIG_NET_STATISTICS_PER_INTERFACE) && \
|
|
defined(CONFIG_NET_SHELL_DYN_CMD_COMPLETION)
|
|
#define STATS_IFACE_CMD &iface_index
|
|
#else
|
|
#define STATS_IFACE_CMD NULL
|
|
#endif /* CONFIG_NET_STATISTICS && CONFIG_NET_STATISTICS_PER_INTERFACE &&
|
|
* CONFIG_NET_SHELL_DYN_CMD_COMPLETION
|
|
*/
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_stats,
|
|
SHELL_CMD(all, NULL,
|
|
"Show network statistics for all network interfaces.",
|
|
cmd_net_stats_all),
|
|
SHELL_CMD(iface, STATS_IFACE_CMD,
|
|
"'net stats <index>' shows network statistics for "
|
|
"one specific network interface.",
|
|
cmd_net_stats_iface),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_tcp,
|
|
SHELL_CMD(connect, NULL,
|
|
"'net tcp connect <address> <port>' connects to TCP peer.",
|
|
cmd_net_tcp_connect),
|
|
SHELL_CMD(send, NULL,
|
|
"'net tcp send <data>' sends data to peer using TCP.",
|
|
cmd_net_tcp_send),
|
|
SHELL_CMD(recv, NULL,
|
|
"'net tcp recv' receives data using TCP.",
|
|
cmd_net_tcp_recv),
|
|
SHELL_CMD(close, NULL,
|
|
"'net tcp close' closes TCP connection.", cmd_net_tcp_close),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_vlan,
|
|
SHELL_CMD(add, NULL,
|
|
"'net vlan add <tag> <index>' adds VLAN tag to the "
|
|
"network interface.",
|
|
cmd_net_vlan_add),
|
|
SHELL_CMD(del, NULL,
|
|
"'net vlan del <tag>' deletes VLAN tag from the network "
|
|
"interface.",
|
|
cmd_net_vlan_del),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_ping,
|
|
SHELL_CMD(--help, NULL,
|
|
"'net ping [-c count] [-i interval ms] [-I <iface index>] <host>' "
|
|
"Send ICMPv4 or ICMPv6 Echo-Request to a network host.",
|
|
cmd_net_ping),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_pkt,
|
|
SHELL_CMD(--help, NULL,
|
|
"'net pkt [ptr in hex]' "
|
|
"Print information about given net_pkt",
|
|
cmd_net_pkt),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_udp,
|
|
SHELL_CMD(bind, NULL,
|
|
"'net udp bind <addr> <port>' binds to UDP local port.",
|
|
cmd_net_udp_bind),
|
|
SHELL_CMD(close, NULL,
|
|
"'net udp close' closes previously bound port.",
|
|
cmd_net_udp_close),
|
|
SHELL_CMD(send, NULL,
|
|
"'net udp send <host> <port> <payload>' "
|
|
"sends UDP packet to a network host.",
|
|
cmd_net_udp_send),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(net_commands,
|
|
SHELL_CMD(allocs, NULL, "Print network memory allocations.",
|
|
cmd_net_allocs),
|
|
SHELL_CMD(arp, &net_cmd_arp, "Print information about IPv4 ARP cache.",
|
|
cmd_net_arp),
|
|
SHELL_CMD(capture, &net_cmd_capture,
|
|
"Configure network packet capture.", cmd_net_capture),
|
|
SHELL_CMD(conn, NULL, "Print information about network connections.",
|
|
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,
|
|
"Print information about network interfaces.",
|
|
cmd_net_iface),
|
|
SHELL_CMD(ipv6, NULL,
|
|
"Print information about IPv6 specific information and "
|
|
"configuration.",
|
|
cmd_net_ipv6),
|
|
SHELL_CMD(mem, NULL, "Print information about network memory usage.",
|
|
cmd_net_mem),
|
|
SHELL_CMD(nbr, &net_cmd_nbr, "Print neighbor information.",
|
|
cmd_net_nbr),
|
|
SHELL_CMD(ping, &net_cmd_ping, "Ping a network host.", cmd_net_ping),
|
|
SHELL_CMD(pkt, &net_cmd_pkt, "net_pkt information.", cmd_net_pkt),
|
|
SHELL_CMD(ppp, &net_cmd_ppp, "PPP information.", cmd_net_ppp_status),
|
|
SHELL_CMD(resume, NULL, "Resume a network interface", cmd_net_resume),
|
|
SHELL_CMD(route, NULL, "Show network route.", cmd_net_route),
|
|
SHELL_CMD(stacks, NULL, "Show network stacks information.",
|
|
cmd_net_stacks),
|
|
SHELL_CMD(stats, &net_cmd_stats, "Show network statistics.",
|
|
cmd_net_stats),
|
|
SHELL_CMD(suspend, NULL, "Suspend a network interface",
|
|
cmd_net_suspend),
|
|
SHELL_CMD(tcp, &net_cmd_tcp, "Connect/send/close TCP connection.",
|
|
cmd_net_tcp),
|
|
SHELL_CMD(udp, &net_cmd_udp, "Send/recv UDP packet", cmd_net_udp),
|
|
SHELL_CMD(virtual, NULL, "Show virtual network interfaces.",
|
|
cmd_net_virtual),
|
|
SHELL_CMD(vlan, &net_cmd_vlan, "Show VLAN information.", cmd_net_vlan),
|
|
SHELL_CMD(websocket, NULL, "Print information about WebSocket "
|
|
"connections.",
|
|
cmd_net_websocket),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
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;
|
|
}
|