This does not save any space but sets the fields in the structs in more natural order. Move IPv6 related fields in net_if into internal ipv6 struct so that all IPv6 fields are located inside one struct. Same thing is done for IPv4 fields which are now located inside IPv4 internal struct in net_if. There is no functionality changes by this commit. Change-Id: I7d72ec0a28e2b88c79a4c294655d5ef6da6ccb25 Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
1592 lines
37 KiB
C
1592 lines
37 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.h>
|
|
#include <stdlib.h>
|
|
#include <shell/shell.h>
|
|
|
|
#include <net/net_if.h>
|
|
#include <net/dns_resolve.h>
|
|
#include <misc/printk.h>
|
|
|
|
#include "route.h"
|
|
#include "icmpv6.h"
|
|
#include "icmpv4.h"
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
#include "tcp.h"
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
#include "ipv6.h"
|
|
#endif
|
|
|
|
#include "net_shell.h"
|
|
#include "net_stats.h"
|
|
|
|
/*
|
|
* Set NET_LOG_ENABLED in order to activate address printing functions
|
|
* in net_private.h
|
|
*/
|
|
#define NET_LOG_ENABLED 1
|
|
#include "net_private.h"
|
|
|
|
#define NET_SHELL_MODULE "net"
|
|
|
|
/* net_stack dedicated section limiters */
|
|
extern struct net_stack_info __net_stack_start[];
|
|
extern struct net_stack_info __net_stack_end[];
|
|
|
|
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";
|
|
}
|
|
|
|
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 void iface_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
struct net_if_ipv6_prefix *prefix;
|
|
struct net_if_router *router;
|
|
#endif
|
|
struct net_if_addr *unicast;
|
|
struct net_if_mcast_addr *mcast;
|
|
int i, count;
|
|
|
|
ARG_UNUSED(user_data);
|
|
|
|
printk("Interface %p\n", iface);
|
|
printk("====================\n");
|
|
|
|
printk("Link addr : %s\n", net_sprint_ll_addr(iface->link_addr.addr,
|
|
iface->link_addr.len));
|
|
printk("MTU : %d\n", iface->mtu);
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
count = 0;
|
|
|
|
printk("IPv6 unicast addresses (max %d):\n", NET_IF_MAX_IPV6_ADDR);
|
|
for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) {
|
|
unicast = &iface->ipv6.unicast[i];
|
|
|
|
if (!unicast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
printk("\t%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" : "");
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
printk("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
printk("IPv6 multicast addresses (max %d):\n", NET_IF_MAX_IPV6_MADDR);
|
|
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
|
|
mcast = &iface->ipv6.mcast[i];
|
|
|
|
if (!mcast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
printk("\t%s\n",
|
|
net_sprint_ipv6_addr(&mcast->address.in6_addr));
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
printk("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
printk("IPv6 prefixes (max %d):\n", NET_IF_MAX_IPV6_PREFIX);
|
|
for (i = 0; i < NET_IF_MAX_IPV6_PREFIX; i++) {
|
|
prefix = &iface->ipv6.prefix[i];
|
|
|
|
if (!prefix->is_used) {
|
|
continue;
|
|
}
|
|
|
|
printk("\t%s/%d%s\n",
|
|
net_sprint_ipv6_addr(&prefix->prefix),
|
|
prefix->len,
|
|
prefix->is_infinite ? " infinite" : "");
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
printk("\t<none>\n");
|
|
}
|
|
|
|
router = net_if_ipv6_router_find_default(iface, NULL);
|
|
if (router) {
|
|
printk("IPv6 default router :\n");
|
|
printk("\t%s%s\n",
|
|
net_sprint_ipv6_addr(&router->address.in6_addr),
|
|
router->is_infinite ? " infinite" : "");
|
|
}
|
|
|
|
printk("IPv6 hop limit : %d\n", iface->ipv6.hop_limit);
|
|
printk("IPv6 base reachable time : %d\n",
|
|
iface->ipv6.base_reachable_time);
|
|
printk("IPv6 reachable time : %d\n", iface->ipv6.reachable_time);
|
|
printk("IPv6 retransmit timer : %d\n", iface->ipv6.retrans_timer);
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
count = 0;
|
|
|
|
printk("IPv4 unicast addresses (max %d):\n", NET_IF_MAX_IPV4_ADDR);
|
|
for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
|
|
unicast = &iface->ipv4.unicast[i];
|
|
|
|
if (!unicast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
printk("\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) {
|
|
printk("\t<none>\n");
|
|
}
|
|
|
|
count = 0;
|
|
|
|
printk("IPv4 multicast addresses (max %d):\n", NET_IF_MAX_IPV4_MADDR);
|
|
for (i = 0; i < NET_IF_MAX_IPV4_MADDR; i++) {
|
|
mcast = &iface->ipv4.mcast[i];
|
|
|
|
if (!mcast->is_used) {
|
|
continue;
|
|
}
|
|
|
|
printk("\t%s\n",
|
|
net_sprint_ipv4_addr(&mcast->address.in_addr));
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
printk("\t<none>\n");
|
|
}
|
|
|
|
printk("IPv4 gateway : %s\n",
|
|
net_sprint_ipv4_addr(&iface->ipv4.gw));
|
|
printk("IPv4 netmask : %s\n",
|
|
net_sprint_ipv4_addr(&iface->ipv4.netmask));
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
printk("DHCPv4 lease time : %u\n", iface->dhcpv4.lease_time);
|
|
printk("DHCPv4 renew time : %u\n", iface->dhcpv4.renewal_time);
|
|
printk("DHCPv4 server : %s\n",
|
|
net_sprint_ipv4_addr(&iface->dhcpv4.server_id));
|
|
printk("DHCPv4 requested : %s\n",
|
|
net_sprint_ipv4_addr(&iface->dhcpv4.requested_ip));
|
|
printk("DHCPv4 state : %s\n",
|
|
net_dhcpv4_state_name(iface->dhcpv4.state));
|
|
printk("DHCPv4 attempts : %d\n", iface->dhcpv4.attempts);
|
|
#endif /* CONFIG_NET_DHCPV4 */
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
static void route_cb(struct net_route_entry *entry, void *user_data)
|
|
{
|
|
struct net_if *iface = user_data;
|
|
struct net_route_nexthop *nexthop_route;
|
|
int count;
|
|
|
|
if (entry->iface != iface) {
|
|
return;
|
|
}
|
|
|
|
printk("IPv6 Route %p for interface %p\n", entry, iface);
|
|
printk("==============================================\n");
|
|
|
|
printk("IPv6 prefix : %s/%d\n",
|
|
net_sprint_ipv6_addr(&entry->addr),
|
|
entry->prefix_len);
|
|
|
|
count = 0;
|
|
|
|
printk("Next hops :\n");
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&entry->nexthop, nexthop_route, node) {
|
|
struct net_linkaddr_storage *lladdr;
|
|
|
|
if (!nexthop_route->nbr) {
|
|
continue;
|
|
}
|
|
|
|
printk("\tneighbor : %p\n", nexthop_route->nbr);
|
|
|
|
if (nexthop_route->nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
printk("\tlink addr : <unknown>\n");
|
|
} else {
|
|
lladdr = net_nbr_get_lladdr(nexthop_route->nbr->idx);
|
|
|
|
printk("\tlink addr : %s\n",
|
|
net_sprint_ll_addr(lladdr->addr,
|
|
lladdr->len));
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
if (count == 0) {
|
|
printk("\t<none>\n");
|
|
}
|
|
}
|
|
|
|
static void iface_per_route_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
ARG_UNUSED(user_data);
|
|
|
|
net_route_foreach(route_cb, iface);
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE */
|
|
|
|
#if defined(CONFIG_NET_ROUTE_MCAST)
|
|
static void route_mcast_cb(struct net_route_entry_mcast *entry,
|
|
void *user_data)
|
|
{
|
|
struct net_if *iface = user_data;
|
|
|
|
if (entry->iface != iface) {
|
|
return;
|
|
}
|
|
|
|
printk("IPv6 multicast route %p for interface %p\n", entry, iface);
|
|
printk("========================================================\n");
|
|
|
|
printk("IPv6 group : %s\n", net_sprint_ipv6_addr(&entry->group));
|
|
printk("Lifetime : %u\n", entry->lifetime);
|
|
}
|
|
|
|
static void iface_per_mcast_route_cb(struct net_if *iface, void *user_data)
|
|
{
|
|
net_route_mcast_foreach(route_mcast_cb, NULL, iface);
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE_MCAST */
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
|
|
static inline void net_shell_print_statistics(void)
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
printk("IPv6 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n",
|
|
GET_STAT(ipv6.recv),
|
|
GET_STAT(ipv6.sent),
|
|
GET_STAT(ipv6.drop),
|
|
GET_STAT(ipv6.forwarded));
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
printk("IPv6 ND recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(ipv6_nd.recv),
|
|
GET_STAT(ipv6_nd.sent),
|
|
GET_STAT(ipv6_nd.drop));
|
|
#endif /* CONFIG_NET_IPV6_ND */
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
printk("IPv4 recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n",
|
|
GET_STAT(ipv4.recv),
|
|
GET_STAT(ipv4.sent),
|
|
GET_STAT(ipv4.drop),
|
|
GET_STAT(ipv4.forwarded));
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
printk("IP vhlerr %d\thblener\t%d\tlblener\t%d\n",
|
|
GET_STAT(ip_errors.vhlerr),
|
|
GET_STAT(ip_errors.hblenerr),
|
|
GET_STAT(ip_errors.lblenerr));
|
|
printk("IP fragerr %d\tchkerr\t%d\tprotoer\t%d\n",
|
|
GET_STAT(ip_errors.fragerr),
|
|
GET_STAT(ip_errors.chkerr),
|
|
GET_STAT(ip_errors.protoerr));
|
|
|
|
printk("ICMP recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(icmp.recv),
|
|
GET_STAT(icmp.sent),
|
|
GET_STAT(icmp.drop));
|
|
printk("ICMP typeer %d\tchkerr\t%d\n",
|
|
GET_STAT(icmp.typeerr),
|
|
GET_STAT(icmp.chkerr));
|
|
|
|
#if defined(CONFIG_NET_UDP)
|
|
printk("UDP recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(udp.recv),
|
|
GET_STAT(udp.sent),
|
|
GET_STAT(udp.drop));
|
|
printk("UDP chkerr %d\n",
|
|
GET_STAT(udp.chkerr));
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_STATISTICS_RPL)
|
|
printk("RPL DIS recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(rpl.dis.recv),
|
|
GET_STAT(rpl.dis.sent),
|
|
GET_STAT(rpl.dis.drop));
|
|
printk("RPL DIO recv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(rpl.dio.recv),
|
|
GET_STAT(rpl.dio.sent),
|
|
GET_STAT(rpl.dio.drop));
|
|
printk("RPL DAO recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n",
|
|
GET_STAT(rpl.dao.recv),
|
|
GET_STAT(rpl.dao.sent),
|
|
GET_STAT(rpl.dao.drop),
|
|
GET_STAT(rpl.dao.forwarded));
|
|
printk("RPL DAOACK rcv %d\tsent\t%d\tdrop\t%d\n",
|
|
GET_STAT(rpl.dao_ack.recv),
|
|
GET_STAT(rpl.dao_ack.sent),
|
|
GET_STAT(rpl.dao_ack.drop));
|
|
printk("RPL overflows %d\tl-repairs\t%d\tg-repairs\t%d\n",
|
|
GET_STAT(rpl.mem_overflows),
|
|
GET_STAT(rpl.local_repairs),
|
|
GET_STAT(rpl.global_repairs));
|
|
printk("RPL malformed %d\tresets \t%d\tp-switch\t%d\n",
|
|
GET_STAT(rpl.malformed_msgs),
|
|
GET_STAT(rpl.resets),
|
|
GET_STAT(rpl.parent_switch));
|
|
printk("RPL f-errors %d\tl-errors\t%d\tl-warnings\t%d\n",
|
|
GET_STAT(rpl.forward_errors),
|
|
GET_STAT(rpl.loop_errors),
|
|
GET_STAT(rpl.loop_warnings));
|
|
printk("RPL r-repairs %d\n",
|
|
GET_STAT(rpl.root_repairs));
|
|
#endif
|
|
|
|
printk("Bytes received %u\n", GET_STAT(bytes.received));
|
|
printk("Bytes sent %u\n", GET_STAT(bytes.sent));
|
|
printk("Processing err %d\n", GET_STAT(processing_error));
|
|
}
|
|
#endif /* CONFIG_NET_STATISTICS */
|
|
|
|
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
|
|
|
|
int *count = user_data;
|
|
char addr_local[ADDR_LEN];
|
|
char addr_remote[ADDR_LEN];
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
if (context->local.family == AF_INET6) {
|
|
snprintk(addr_local, ADDR_LEN, "%s",
|
|
net_sprint_ipv6_addr(
|
|
net_sin6_ptr(&context->local)->sin6_addr));
|
|
snprintk(addr_remote, ADDR_LEN, "%s",
|
|
net_sprint_ipv6_addr(
|
|
&net_sin6(&context->remote)->sin6_addr));
|
|
} else
|
|
#endif
|
|
#if defined(CONFIG_NET_IPV4)
|
|
if (context->local.family == AF_INET) {
|
|
snprintk(addr_local, ADDR_LEN, "%s",
|
|
net_sprint_ipv4_addr(
|
|
net_sin_ptr(&context->local)->sin_addr));
|
|
snprintk(addr_remote, ADDR_LEN, "%s",
|
|
net_sprint_ipv4_addr(
|
|
&net_sin(&context->remote)->sin_addr));
|
|
} else
|
|
#endif
|
|
if (context->local.family == AF_UNSPEC) {
|
|
printk("Network address family not set for context %p\n",
|
|
context);
|
|
return;
|
|
} else {
|
|
printk("Invalid address family (%d) for context %p\n",
|
|
context->local.family, context);
|
|
return;
|
|
}
|
|
|
|
printk("[%2d] %p\t%p %c%c%c %16s\t%16s\n",
|
|
(*count) + 1, context,
|
|
net_context_get_iface(context),
|
|
net_context_get_family(context) == AF_INET6 ? '6' : '4',
|
|
net_context_get_type(context) == SOCK_DGRAM ? 'D' : 'S',
|
|
net_context_get_ip_proto(context) == IPPROTO_UDP ?
|
|
'U' : 'T',
|
|
addr_local, addr_remote);
|
|
|
|
(*count)++;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
static void tcp_cb(struct net_tcp *tcp, void *user_data)
|
|
{
|
|
int *count = user_data;
|
|
uint16_t recv_mss = net_tcp_get_recv_mss(tcp);
|
|
|
|
printk("%p %5u %5u %10u %10u %5u %s\n",
|
|
tcp,
|
|
ntohs(net_sin6_ptr(&tcp->context->local)->sin6_port),
|
|
ntohs(net_sin6(&tcp->context->remote)->sin6_port),
|
|
tcp->send_seq, tcp->send_ack, recv_mss,
|
|
net_tcp_state_str(net_tcp_get_state(tcp)));
|
|
|
|
(*count)++;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
static void ipv6_frag_cb(struct net_ipv6_reassembly *reass,
|
|
void *user_data)
|
|
{
|
|
int *count = user_data;
|
|
char src[ADDR_LEN];
|
|
|
|
if (!*count) {
|
|
printk("\nIPv6 reassembly Id Remain Src\t\t\t\tDst\n");
|
|
}
|
|
|
|
snprintk(src, ADDR_LEN, "%s", net_sprint_ipv6_addr(&reass->src));
|
|
|
|
printk("%p 0x%08x %5d %s\t%s\n",
|
|
reass, reass->id, k_delayed_work_remaining_get(&reass->timer),
|
|
src, net_sprint_ipv6_addr(&reass->dst));
|
|
|
|
(*count)++;
|
|
}
|
|
#endif /* CONFIG_NET_IPV6_FRAGMENT */
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_BUF)
|
|
static void allocs_cb(struct net_buf *buf,
|
|
const char *func_alloc,
|
|
int line_alloc,
|
|
const char *func_free,
|
|
int line_free,
|
|
bool in_use,
|
|
void *user_data)
|
|
{
|
|
const char *str;
|
|
|
|
if (in_use) {
|
|
str = "used";
|
|
} else {
|
|
if (func_alloc) {
|
|
str = "free";
|
|
} else {
|
|
str = "avail";
|
|
}
|
|
}
|
|
|
|
if (func_alloc) {
|
|
if (in_use) {
|
|
printk("%p/%d\t%5s\t%5s\t%s():%d\n", buf, buf->ref,
|
|
str, net_nbuf_pool2str(buf->pool), func_alloc,
|
|
line_alloc);
|
|
} else {
|
|
printk("%p\t%5s\t%5s\t%s():%d -> %s():%d\n", buf,
|
|
str, net_nbuf_pool2str(buf->pool), func_alloc,
|
|
line_alloc, func_free, line_free);
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_DEBUG_NET_BUF */
|
|
|
|
/* Put the actual shell commands after this */
|
|
|
|
static int shell_cmd_allocs(int argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_BUF)
|
|
printk("Network buffer allocations\n\n");
|
|
printk("net_buf\t\tStatus\tPool\tFunction alloc -> freed\n");
|
|
net_nbuf_allocs_foreach(allocs_cb, NULL);
|
|
#else
|
|
printk("Enable CONFIG_NET_DEBUG_NET_BUF to see allocations.\n");
|
|
#endif /* CONFIG_NET_DEBUG_NET_BUF */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int shell_cmd_conn(int argc, char *argv[])
|
|
{
|
|
int count = 0;
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
printk(" Context \tIface Flags "
|
|
"Local \tRemote\n");
|
|
|
|
net_context_foreach(context_cb, &count);
|
|
|
|
if (count == 0) {
|
|
printk("No connections\n");
|
|
}
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
printk("\nTCP Src port Dst port Send-Seq Send-Ack MSS "
|
|
"State\n");
|
|
|
|
count = 0;
|
|
|
|
net_tcp_foreach(tcp_cb, &count);
|
|
|
|
if (count == 0) {
|
|
printk("No TCP connections\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
count = 0;
|
|
|
|
net_ipv6_frag_foreach(ipv6_frag_cb, &count);
|
|
|
|
/* Do not print anything if no fragments are pending atm */
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
static void dns_result_cb(enum dns_resolve_status status,
|
|
struct dns_addrinfo *info,
|
|
void *user_data)
|
|
{
|
|
bool *first = user_data;
|
|
|
|
if (status == DNS_EAI_CANCELED) {
|
|
printk("\nTimeout while resolving name.\n");
|
|
*first = false;
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_INPROGRESS && info) {
|
|
char addr[NET_IPV6_ADDR_LEN];
|
|
|
|
if (*first) {
|
|
printk("\n");
|
|
*first = false;
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
printk("\t%s\n", addr);
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_ALLDONE) {
|
|
printk("All results received\n");
|
|
*first = false;
|
|
return;
|
|
}
|
|
|
|
if (status == DNS_EAI_FAIL) {
|
|
printk("No such name found.\n");
|
|
*first = false;
|
|
return;
|
|
}
|
|
|
|
printk("Unhandled status %d received\n", status);
|
|
}
|
|
|
|
static void print_dns_info(struct dns_resolve_context *ctx)
|
|
{
|
|
int i;
|
|
|
|
printk("DNS servers:\n");
|
|
|
|
for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) {
|
|
if (ctx->servers[i].dns_server.family == AF_INET) {
|
|
printk("\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.family == AF_INET6) {
|
|
printk("\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));
|
|
}
|
|
}
|
|
|
|
printk("Pending queries:\n");
|
|
|
|
for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) {
|
|
int32_t remaining;
|
|
|
|
if (!ctx->queries[i].cb) {
|
|
continue;
|
|
}
|
|
|
|
remaining =
|
|
k_delayed_work_remaining_get(&ctx->queries[i].timer);
|
|
|
|
if (ctx->queries[i].query_type == DNS_QUERY_TYPE_A) {
|
|
printk("\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) {
|
|
printk("\tIPv6[%u]: %s remaining %d\n",
|
|
ctx->queries[i].id,
|
|
ctx->queries[i].query,
|
|
remaining);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int shell_cmd_dns(int argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
#define DNS_TIMEOUT 2000 /* ms */
|
|
|
|
struct dns_resolve_context *ctx;
|
|
enum dns_query_type qtype = DNS_QUERY_TYPE_A;
|
|
char *host, *type = NULL;
|
|
bool first = true;
|
|
int arg = 1;
|
|
int ret, i;
|
|
|
|
if (strcmp(argv[0], "dns")) {
|
|
arg++;
|
|
}
|
|
|
|
if (!argv[arg]) {
|
|
/* DNS status */
|
|
ctx = dns_resolve_get_default();
|
|
if (!ctx) {
|
|
printk("No default DNS context found.\n");
|
|
return 0;
|
|
}
|
|
|
|
print_dns_info(ctx);
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(argv[arg], "cancel") == 0) {
|
|
ctx = dns_resolve_get_default();
|
|
if (!ctx) {
|
|
printk("No default DNS context found.\n");
|
|
return 0;
|
|
}
|
|
|
|
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) {
|
|
printk("Cancelled %d pending requests.\n", ret);
|
|
} else {
|
|
printk("No pending DNS requests.\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
host = argv[arg++];
|
|
|
|
if (argv[arg]) {
|
|
type = argv[arg];
|
|
}
|
|
|
|
if (type) {
|
|
if (strcmp(type, "A") == 0) {
|
|
qtype = DNS_QUERY_TYPE_A;
|
|
printk("IPv4 address type\n");
|
|
} else if (strcmp(type, "AAAA") == 0) {
|
|
qtype = DNS_QUERY_TYPE_AAAA;
|
|
printk("IPv6 address type\n");
|
|
} else {
|
|
printk("Unknown query type, specify either "
|
|
"A or AAAA\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ret = dns_get_addr_info(host, qtype, NULL, dns_result_cb, &first,
|
|
DNS_TIMEOUT);
|
|
if (ret < 0) {
|
|
printk("Cannot resolve '%s' (%d)\n", host, ret);
|
|
} else {
|
|
printk("Query for '%s' sent.\n", host);
|
|
}
|
|
|
|
#else
|
|
printk("DNS resolver not supported.\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int shell_cmd_iface(int argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
net_if_foreach(iface_cb, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct ctx_info {
|
|
int pos;
|
|
bool are_external_pools;
|
|
struct net_buf_pool *tx_pools[CONFIG_NET_MAX_CONTEXTS];
|
|
struct net_buf_pool *data_pools[CONFIG_NET_MAX_CONTEXTS];
|
|
};
|
|
|
|
#if defined(CONFIG_NET_CONTEXT_NBUF_POOL)
|
|
static bool pool_found_already(struct ctx_info *info,
|
|
struct net_buf_pool *pool)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CONFIG_NET_MAX_CONTEXTS; i++) {
|
|
if (info->tx_pools[i] == pool ||
|
|
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_NBUF_POOL)
|
|
struct ctx_info *info = user_data;
|
|
struct net_buf_pool *pool;
|
|
|
|
if (!net_context_is_used(context)) {
|
|
return;
|
|
}
|
|
|
|
if (context->tx_pool) {
|
|
pool = context->tx_pool();
|
|
|
|
if (pool_found_already(info, pool)) {
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_BUF)
|
|
printk("ETX (%s)\t%d\t%d\t%d\t%p\n",
|
|
pool->name, pool->pool_size, pool->buf_count,
|
|
pool->avail_count, pool);
|
|
#else
|
|
printk("ETX \t%d\t%p\n", pool->buf_count, pool);
|
|
#endif
|
|
info->are_external_pools = true;
|
|
info->tx_pools[info->pos] = pool;
|
|
}
|
|
|
|
if (context->data_pool) {
|
|
pool = context->data_pool();
|
|
|
|
if (pool_found_already(info, pool)) {
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_BUF)
|
|
printk("EDATA (%s)\t%d\t%d\t%d\t%p\n",
|
|
pool->name, pool->pool_size, pool->buf_count,
|
|
pool->avail_count, pool);
|
|
#else
|
|
printk("EDATA \t%d\t%p\n", pool->buf_count, pool);
|
|
#endif
|
|
info->are_external_pools = true;
|
|
info->data_pools[info->pos] = pool;
|
|
}
|
|
|
|
info->pos++;
|
|
#endif /* CONFIG_NET_CONTEXT_NBUF_POOL */
|
|
}
|
|
|
|
static int shell_cmd_mem(int argc, char *argv[])
|
|
{
|
|
struct net_buf_pool *tx, *rx, *rx_data, *tx_data;
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
net_nbuf_get_info(&rx, &tx, &rx_data, &tx_data);
|
|
|
|
printk("Fragment length %d bytes\n", CONFIG_NET_NBUF_DATA_SIZE);
|
|
|
|
printk("Network buffer pools:\n");
|
|
|
|
#if defined(CONFIG_NET_DEBUG_NET_BUF)
|
|
printk("Name\t\t\tSize\tCount\tAvail\tAddress\n");
|
|
|
|
printk("RX (%s)\t\t%d\t%d\t%d\t%p\n",
|
|
rx->name, rx->pool_size, rx->buf_count, rx->avail_count, rx);
|
|
|
|
printk("TX (%s)\t\t%d\t%d\t%d\t%p\n",
|
|
tx->name, tx->pool_size, tx->buf_count, tx->avail_count, tx);
|
|
|
|
printk("RX DATA (%s)\t%d\t%d\t%d\t%p\n",
|
|
rx_data->name, rx_data->pool_size, rx_data->buf_count,
|
|
rx_data->avail_count, rx_data);
|
|
|
|
printk("TX DATA (%s)\t%d\t%d\t%d\t%p\n",
|
|
tx_data->name, tx_data->pool_size, tx_data->buf_count,
|
|
tx_data->avail_count, tx_data);
|
|
#else
|
|
printk("Name \tCount\tAddress\n");
|
|
|
|
printk("RX \t%d\t%p\n", rx->buf_count, rx);
|
|
printk("TX \t%d\t%p\n", tx->buf_count, tx);
|
|
printk("RX DATA \t%d\t%p\n", rx_data->buf_count, rx_data);
|
|
printk("TX DATA \t%d\t%p\n", tx_data->buf_count, tx_data);
|
|
#endif /* CONFIG_NET_DEBUG_NET_BUF */
|
|
|
|
if (IS_ENABLED(CONFIG_NET_CONTEXT_NBUF_POOL)) {
|
|
struct ctx_info info;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
net_context_foreach(context_info, &info);
|
|
|
|
if (!info.are_external_pools) {
|
|
printk("No external memory pools found.\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
static void nbr_cb(struct net_nbr *nbr, void *user_data)
|
|
{
|
|
int *count = user_data;
|
|
|
|
if (*count == 0) {
|
|
printk(" Neighbor Flags Interface State\t"
|
|
"Remain\tLink Address\n");
|
|
}
|
|
|
|
(*count)++;
|
|
|
|
printk("[%2d] %p %d/%d/%d/%d %p %9s\t%6d\t%17s %s\n",
|
|
*count, nbr, nbr->ref, net_ipv6_nbr_data(nbr)->ns_count,
|
|
net_ipv6_nbr_data(nbr)->is_router,
|
|
net_ipv6_nbr_data(nbr)->link_metric,
|
|
nbr->iface,
|
|
net_ipv6_nbr_state2str(net_ipv6_nbr_data(nbr)->state),
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
k_delayed_work_remaining_get(
|
|
&net_ipv6_nbr_data(nbr)->reachable),
|
|
#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),
|
|
net_sprint_ipv6_addr(&net_ipv6_nbr_data(nbr)->addr));
|
|
}
|
|
#endif
|
|
|
|
static int shell_cmd_nbr(int argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_IPV6)
|
|
int count = 0;
|
|
int arg = 1;
|
|
|
|
if (strcmp(argv[0], "nbr")) {
|
|
arg++;
|
|
}
|
|
|
|
if (argv[arg]) {
|
|
struct in6_addr addr;
|
|
int ret;
|
|
|
|
if (strcmp(argv[arg], "rm")) {
|
|
printk("Unknown command '%s'\n", argv[arg]);
|
|
return 0;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
printk("Neighbor IPv6 address missing.\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = net_addr_pton(AF_INET6, argv[arg], &addr);
|
|
if (ret < 0) {
|
|
printk("Cannot parse '%s'\n", argv[arg]);
|
|
return 0;
|
|
}
|
|
|
|
if (!net_ipv6_nbr_rm(net_if_get_default(), &addr)) {
|
|
printk("Cannot remove neighbor %s\n",
|
|
net_sprint_ipv6_addr(&addr));
|
|
} else {
|
|
printk("Neighbor %s removed.\n",
|
|
net_sprint_ipv6_addr(&addr));
|
|
}
|
|
}
|
|
|
|
net_ipv6_nbr_foreach(nbr_cb, &count);
|
|
|
|
if (count == 0) {
|
|
printk("No neighbors.\n");
|
|
}
|
|
#else
|
|
printk("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);
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
static enum net_verdict _handle_ipv6_echo_reply(struct net_buf *buf);
|
|
|
|
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_buf *buf)
|
|
{
|
|
char addr[NET_IPV6_ADDR_LEN];
|
|
|
|
snprintk(addr, sizeof(addr), "%s",
|
|
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->dst));
|
|
|
|
printk("Received echo reply from %s to %s\n",
|
|
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src), addr);
|
|
|
|
k_sem_give(&ping_timeout);
|
|
_remove_ipv6_ping_handler();
|
|
|
|
return NET_OK;
|
|
}
|
|
|
|
static int _ping_ipv6(char *host)
|
|
{
|
|
struct in6_addr ipv6_target;
|
|
int ret;
|
|
|
|
if (net_addr_pton(AF_INET6, host, &ipv6_target) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_icmpv6_register_handler(&ping6_handler);
|
|
|
|
ret = net_icmpv6_send_echo_request(net_if_get_default(),
|
|
&ipv6_target,
|
|
sys_rand32_get(),
|
|
sys_rand32_get());
|
|
if (ret) {
|
|
_remove_ipv6_ping_handler();
|
|
} else {
|
|
printk("Sent a ping to %s\n", host);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define _ping_ipv6(...) -EINVAL
|
|
#define _remove_ipv6_ping_handler()
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
static enum net_verdict _handle_ipv4_echo_reply(struct net_buf *buf);
|
|
|
|
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_buf *buf)
|
|
{
|
|
char addr[NET_IPV4_ADDR_LEN];
|
|
|
|
snprintk(addr, sizeof(addr), "%s",
|
|
net_sprint_ipv4_addr(&NET_IPV4_BUF(buf)->dst));
|
|
|
|
printk("Received echo reply from %s to %s\n",
|
|
net_sprint_ipv4_addr(&NET_IPV4_BUF(buf)->src), addr);
|
|
|
|
k_sem_give(&ping_timeout);
|
|
_remove_ipv4_ping_handler();
|
|
|
|
return NET_OK;
|
|
}
|
|
|
|
static int _ping_ipv4(char *host)
|
|
{
|
|
struct in_addr ipv4_target;
|
|
int ret;
|
|
|
|
if (net_addr_pton(AF_INET, host, &ipv4_target) < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_icmpv4_register_handler(&ping4_handler);
|
|
|
|
ret = net_icmpv4_send_echo_request(net_if_get_default(),
|
|
&ipv4_target,
|
|
sys_rand32_get(),
|
|
sys_rand32_get());
|
|
if (ret) {
|
|
_remove_ipv4_ping_handler();
|
|
} else {
|
|
printk("Sent a ping to %s\n", host);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define _ping_ipv4(...) -EINVAL
|
|
#define _remove_ipv4_ping_handler()
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */
|
|
|
|
static int shell_cmd_ping(int argc, char *argv[])
|
|
{
|
|
char *host;
|
|
int ret;
|
|
|
|
ARG_UNUSED(argc);
|
|
|
|
if (!strcmp(argv[0], "ping")) {
|
|
host = argv[1];
|
|
} else {
|
|
host = argv[2];
|
|
}
|
|
|
|
ret = _ping_ipv6(host);
|
|
if (!ret) {
|
|
goto wait_reply;
|
|
} else if (ret == -EIO) {
|
|
printk("Cannot send IPv6 ping\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = _ping_ipv4(host);
|
|
if (ret) {
|
|
if (ret == -EIO) {
|
|
printk("Cannot send IPv4 ping\n");
|
|
} else if (ret == -EINVAL) {
|
|
printk("Invalid IP address\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
wait_reply:
|
|
ret = k_sem_take(&ping_timeout, K_SECONDS(2));
|
|
if (ret == -EAGAIN) {
|
|
printk("Ping timeout\n");
|
|
_remove_ipv6_ping_handler();
|
|
_remove_ipv4_ping_handler();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int shell_cmd_route(int argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
net_if_foreach(iface_per_route_cb, NULL);
|
|
#else
|
|
printk("Network route support not compiled in.\n");
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_ROUTE_MCAST)
|
|
net_if_foreach(iface_per_mcast_route_cb, NULL);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_INIT_STACKS)
|
|
extern char _main_stack[];
|
|
extern char _interrupt_stack[];
|
|
#endif
|
|
|
|
static int shell_cmd_stacks(int argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_INIT_STACKS)
|
|
unsigned int stack_offset, pcnt, unused;
|
|
#endif
|
|
struct net_stack_info *info;
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
for (info = __net_stack_start; info != __net_stack_end; info++) {
|
|
net_analyze_stack_get_values(info->stack, info->size,
|
|
&stack_offset, &pcnt, &unused);
|
|
|
|
#if defined(CONFIG_INIT_STACKS)
|
|
printk("%s [%s] stack size %zu/%zu bytes unused %u usage"
|
|
" %zu/%zu (%u %%)\n",
|
|
info->pretty_name, info->name, info->orig_size,
|
|
info->size + stack_offset, unused,
|
|
info->size - unused, info->size, pcnt);
|
|
#else
|
|
printk("%s [%s] stack size %u usage not available\n",
|
|
info->pretty_name, info->name, info->orig_size);
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_INIT_STACKS)
|
|
net_analyze_stack_get_values(_main_stack, CONFIG_MAIN_STACK_SIZE,
|
|
&stack_offset, &pcnt, &unused);
|
|
printk("%s [%s] stack size %d/%d bytes unused %u usage"
|
|
" %d/%d (%u %%)\n",
|
|
"main", "_main_stack", CONFIG_MAIN_STACK_SIZE,
|
|
CONFIG_MAIN_STACK_SIZE + stack_offset, unused,
|
|
CONFIG_MAIN_STACK_SIZE - unused, CONFIG_MAIN_STACK_SIZE, pcnt);
|
|
|
|
net_analyze_stack_get_values(_interrupt_stack, CONFIG_ISR_STACK_SIZE,
|
|
&stack_offset, &pcnt, &unused);
|
|
printk("%s [%s] stack size %d/%d bytes unused %u usage"
|
|
" %d/%d (%u %%)\n",
|
|
"ISR", "_interrupt_stack", CONFIG_ISR_STACK_SIZE,
|
|
CONFIG_ISR_STACK_SIZE + stack_offset, unused,
|
|
CONFIG_ISR_STACK_SIZE - unused, CONFIG_ISR_STACK_SIZE, pcnt);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int shell_cmd_stats(int argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
net_shell_print_statistics();
|
|
#else
|
|
printk("Network statistics not compiled in.\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
static struct net_context *tcp_ctx;
|
|
|
|
#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) {
|
|
printk("TCP connection failed (%d)\n", status);
|
|
|
|
net_context_put(context);
|
|
|
|
tcp_ctx = NULL;
|
|
} else {
|
|
printk("TCP connected\n");
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
static void get_my_ipv6_addr(struct net_if *iface,
|
|
struct sockaddr *myaddr)
|
|
{
|
|
const struct in6_addr *my6addr;
|
|
|
|
my6addr = net_if_ipv6_select_src_addr(net_if_get_default(),
|
|
&net_sin6(myaddr)->sin6_addr);
|
|
|
|
memcpy(&net_sin6(myaddr)->sin6_addr, my6addr, sizeof(struct in6_addr));
|
|
|
|
net_sin6(myaddr)->sin6_port = 0; /* let the IP stack to select */
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
static void get_my_ipv4_addr(struct net_if *iface,
|
|
struct sockaddr *myaddr)
|
|
{
|
|
/* Just take the first IPv4 address of an interface. */
|
|
memcpy(&net_sin(myaddr)->sin_addr,
|
|
&iface->ipv4.unicast[0].address.in_addr,
|
|
sizeof(struct in_addr));
|
|
|
|
net_sin(myaddr)->sin_port = 0; /* let the IP stack to select */
|
|
}
|
|
#endif
|
|
|
|
static void print_connect_info(int family,
|
|
struct sockaddr *myaddr,
|
|
struct sockaddr *addr)
|
|
{
|
|
switch (family) {
|
|
case AF_INET:
|
|
#if defined(CONFIG_NET_IPV4)
|
|
printk("Connecting from %s:%u ",
|
|
net_sprint_ipv4_addr(&net_sin(myaddr)->sin_addr),
|
|
ntohs(net_sin(myaddr)->sin_port));
|
|
printk("to %s:%u\n",
|
|
net_sprint_ipv4_addr(&net_sin(addr)->sin_addr),
|
|
ntohs(net_sin(addr)->sin_port));
|
|
#else
|
|
printk("IPv4 not supported\n");
|
|
#endif
|
|
break;
|
|
|
|
case AF_INET6:
|
|
#if defined(CONFIG_NET_IPV6)
|
|
printk("Connecting from [%s]:%u ",
|
|
net_sprint_ipv6_addr(&net_sin6(myaddr)->sin6_addr),
|
|
ntohs(net_sin6(myaddr)->sin6_port));
|
|
printk("to [%s]:%u\n",
|
|
net_sprint_ipv6_addr(&net_sin6(addr)->sin6_addr),
|
|
ntohs(net_sin6(addr)->sin6_port));
|
|
#else
|
|
printk("IPv6 not supported\n");
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
printk("Unknown protocol family (%d)\n", family);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int tcp_connect(char *host, uint16_t port, struct net_context **ctx)
|
|
{
|
|
struct sockaddr addr;
|
|
struct sockaddr myaddr;
|
|
int addrlen;
|
|
int family;
|
|
int ret;
|
|
|
|
#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
|
|
ret = net_addr_pton(AF_INET6, host, &net_sin6(&addr)->sin6_addr);
|
|
if (ret < 0) {
|
|
printk("Invalid IPv6 address\n");
|
|
return 0;
|
|
}
|
|
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
get_my_ipv6_addr(net_if_get_default(), &myaddr);
|
|
family = addr.family = myaddr.family = AF_INET6;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
|
|
ret = net_addr_pton(AF_INET, host, &net_sin(&addr)->sin_addr);
|
|
if (ret < 0) {
|
|
printk("Invalid IPv4 address\n");
|
|
return 0;
|
|
}
|
|
|
|
get_my_ipv4_addr(net_if_get_default(), &myaddr);
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
family = addr.family = myaddr.family = AF_INET;
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6) && defined(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) {
|
|
printk("Invalid IP address\n");
|
|
return 0;
|
|
}
|
|
|
|
net_sin(&addr)->sin_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in);
|
|
get_my_ipv4_addr(net_if_get_default(), &myaddr);
|
|
family = addr.family = myaddr.family = AF_INET;
|
|
} else {
|
|
net_sin6(&addr)->sin6_port = htons(port);
|
|
addrlen = sizeof(struct sockaddr_in6);
|
|
get_my_ipv6_addr(net_if_get_default(), &myaddr);
|
|
family = addr.family = myaddr.family = AF_INET6;
|
|
}
|
|
#endif
|
|
|
|
print_connect_info(family, &myaddr, &addr);
|
|
|
|
ret = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, ctx);
|
|
if (ret < 0) {
|
|
printk("Cannot get TCP context (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = net_context_bind(*ctx, &myaddr, addrlen);
|
|
if (ret < 0) {
|
|
printk("Cannot bind TCP (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return net_context_connect(*ctx, &addr, addrlen, tcp_connected,
|
|
K_NO_WAIT, NULL);
|
|
}
|
|
|
|
static void tcp_sent_cb(struct net_context *context,
|
|
int status,
|
|
void *token,
|
|
void *user_data)
|
|
{
|
|
printk("Message sent\n");
|
|
}
|
|
#endif
|
|
|
|
static int shell_cmd_tcp(int argc, char *argv[])
|
|
{
|
|
#if defined(CONFIG_NET_TCP)
|
|
int arg = 1;
|
|
int ret;
|
|
|
|
if (strcmp(argv[0], "tcp")) {
|
|
arg++;
|
|
}
|
|
|
|
if (argv[arg]) {
|
|
if (!strcmp(argv[arg], "connect")) {
|
|
/* tcp connect <ip> port */
|
|
char *ip;
|
|
uint16_t port;
|
|
|
|
if (tcp_ctx && net_context_is_used(tcp_ctx)) {
|
|
printk("Already connected\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
printk("Peer IP address missing.\n");
|
|
return 0;
|
|
}
|
|
|
|
ip = argv[arg];
|
|
|
|
if (!argv[++arg]) {
|
|
printk("Peer port missing.\n");
|
|
return 0;
|
|
}
|
|
|
|
port = strtol(argv[arg], NULL, 10);
|
|
|
|
return tcp_connect(ip, port, &tcp_ctx);
|
|
}
|
|
|
|
if (!strcmp(argv[arg], "send")) {
|
|
/* tcp send <data> */
|
|
struct net_buf *buf;
|
|
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
printk("Not connected\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!argv[++arg]) {
|
|
printk("No data to send.\n");
|
|
return 0;
|
|
}
|
|
|
|
buf = net_nbuf_get_tx(tcp_ctx, TCP_TIMEOUT);
|
|
if (!buf) {
|
|
printk("Out of bufs, msg cannot be sent.\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = net_nbuf_append(buf, strlen(argv[arg]),
|
|
argv[arg], TCP_TIMEOUT);
|
|
if (!ret) {
|
|
printk("Cannot build msg (out of bufs)\n");
|
|
net_nbuf_unref(buf);
|
|
return 0;
|
|
}
|
|
|
|
ret = net_context_send(buf, tcp_sent_cb, TCP_TIMEOUT,
|
|
NULL, NULL);
|
|
if (ret < 0) {
|
|
printk("Cannot send msg (%d)\n", ret);
|
|
net_nbuf_unref(buf);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp(argv[arg], "close")) {
|
|
/* tcp close */
|
|
if (!tcp_ctx || !net_context_is_used(tcp_ctx)) {
|
|
printk("Not connected\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = net_context_put(tcp_ctx);
|
|
if (ret < 0) {
|
|
printk("Cannot close the connection (%d)\n",
|
|
ret);
|
|
return 0;
|
|
}
|
|
|
|
printk("Connection closed.\n");
|
|
tcp_ctx = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
printk("Unknown command '%s'\n", argv[arg]);
|
|
goto usage;
|
|
} else {
|
|
printk("Invalid command.\n");
|
|
usage:
|
|
printk("Usage:\n");
|
|
printk("\ttcp connect <ipaddr> port\n");
|
|
printk("\ttcp send <data>\n");
|
|
printk("\ttcp close\n");
|
|
}
|
|
#else
|
|
printk("TCP not enabled.\n");
|
|
#endif /* CONFIG_NET_TCP */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int shell_cmd_help(int argc, char *argv[])
|
|
{
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
/* Keep the commands in alphabetical order */
|
|
printk("net allocs\n\tPrint network buffer allocations\n");
|
|
printk("net conn\n\tPrint information about network connections\n");
|
|
printk("net dns\n\tShow how DNS is configured\n");
|
|
printk("net dns cancel\n\tCancel all pending requests\n");
|
|
printk("net dns <hostname> [A or AAAA]\n\tQuery IPv4 address (default)"
|
|
" or IPv6 address for a host name\n");
|
|
printk("net iface\n\tPrint information about network interfaces\n");
|
|
printk("net mem\n\tPrint network buffer information\n");
|
|
printk("net nbr\n\tPrint neighbor information\n");
|
|
printk("net nbr rm <IPv6 address>\n\tRemove neighbor from cache\n");
|
|
printk("net ping <host>\n\tPing a network host\n");
|
|
printk("net route\n\tShow network routes\n");
|
|
printk("net stacks\n\tShow network stacks information\n");
|
|
printk("net stats\n\tShow network statistics\n");
|
|
printk("net tcp connect <ip> port\n\tConnect to TCP peer\n");
|
|
printk("net tcp send <data>\n\tSend data to peer using TCP\n");
|
|
printk("net tcp close\n\tClose TCP connection\n");
|
|
return 0;
|
|
}
|
|
|
|
static struct shell_cmd net_commands[] = {
|
|
/* Keep the commands in alphabetical order */
|
|
{ "allocs", shell_cmd_allocs, NULL },
|
|
{ "conn", shell_cmd_conn, NULL },
|
|
{ "dns", shell_cmd_dns, NULL },
|
|
{ "help", shell_cmd_help, NULL },
|
|
{ "iface", shell_cmd_iface, NULL },
|
|
{ "mem", shell_cmd_mem, NULL },
|
|
{ "nbr", shell_cmd_nbr, NULL },
|
|
{ "ping", shell_cmd_ping, NULL },
|
|
{ "route", shell_cmd_route, NULL },
|
|
{ "stacks", shell_cmd_stacks, NULL },
|
|
{ "stats", shell_cmd_stats, NULL },
|
|
{ "tcp", shell_cmd_tcp, NULL },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
void net_shell_init(void)
|
|
{
|
|
SHELL_REGISTER(NET_SHELL_MODULE, net_commands);
|
|
}
|