/** @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 #include #include #include #include #include #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 ""; case NET_ADDR_AUTOCONF: return "autoconf"; case NET_ADDR_DHCP: return "DHCP"; case NET_ADDR_MANUAL: return "manual"; } return ""; } static inline const char *addrstate2str(enum net_addr_state addr_state) { switch (addr_state) { case NET_ADDR_ANY_STATE: return ""; case NET_ADDR_TENTATIVE: return "tentative"; case NET_ADDR_PREFERRED: return "preferred"; case NET_ADDR_DEPRECATED: return "deprecated"; } return ""; } 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\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\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\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->hop_limit); printk("IPv6 base reachable time : %d\n", iface->base_reachable_time); printk("IPv6 reachable time : %d\n", iface->reachable_time); printk("IPv6 retransmit timer : %d\n", iface->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\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\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 : \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\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 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 */ 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 port\n"); printk("\ttcp send \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 [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 \n\tRemove neighbor from cache\n"); printk("net ping \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 port\n\tConnect to TCP peer\n"); printk("net tcp send \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); }