| /* |
| * Copyright (c) 2016 Intel Corporation |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_DECLARE(net_shell); |
| |
| #include "net_shell_private.h" |
| |
| static int cmd_net_nbr_rm(const struct shell *sh, 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; |
| } |
| |
| 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 *sh = data->sh; |
| 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 *sh, 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.sh = sh; |
| 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_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 |
| ); |
| |
| SHELL_SUBCMD_ADD((net), nbr, &net_cmd_nbr, |
| "Print neighbor information.", |
| cmd_net_nbr, 1, 0); |