/* * Copyright (c) 2016 Intel Corporation * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_DECLARE(net_shell); #include "net_shell_private.h" #include "../ip/route.h" #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 *sh = data->sh; 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 : \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\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 *sh = data->sh; 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 *sh = data->sh; PR("IPv6 multicast route (%p)\n", entry); PR("=================================\n"); 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); for (int i = 0; i < CONFIG_NET_MCAST_ROUTE_MAX_IFACES; ++i) { if (entry->ifaces[i]) { PR("Interface : %d (%p) %s\n", net_if_get_by_iface(entry->ifaces[i]), entry->ifaces[i], iface2str(entry->ifaces[i], NULL)); } } } #endif /* CONFIG_NET_ROUTE_MCAST */ static int cmd_net_ip6_route_add(const struct shell *sh, size_t argc, char *argv[]) { #if defined(CONFIG_NET_NATIVE_IPV6) && (CONFIG_NET_ROUTE) struct net_if *iface = NULL; int idx; struct net_route_entry *route; struct in6_addr gw = {0}; struct in6_addr prefix = {0}; if (argc != 4) { PR_ERROR("Correct usage: net route add " " \n"); return -EINVAL; } idx = get_iface_idx(sh, 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_addr_pton(AF_INET6, argv[2], &prefix)) { PR_ERROR("Invalid address: %s\n", argv[2]); return -EINVAL; } if (net_addr_pton(AF_INET6, argv[3], &gw)) { PR_ERROR("Invalid gateway: %s\n", argv[3]); return -EINVAL; } route = net_route_add(iface, &prefix, NET_IPV6_DEFAULT_PREFIX_LEN, &gw, NET_IPV6_ND_INFINITE_LIFETIME, NET_ROUTE_PREFERENCE_MEDIUM); if (route == NULL) { PR_ERROR("Failed to add route\n"); return -ENOEXEC; } #else /* CONFIG_NET_NATIVE_IPV6 && CONFIG_NET_ROUTE */ PR_INFO("Set %s and %s to enable native %s support." " And enable CONFIG_NET_ROUTE.\n", "CONFIG_NET_NATIVE", "CONFIG_NET_IPV6", "IPv6"); #endif /* CONFIG_NET_NATIVE_IPV6 && CONFIG_NET_ROUTE */ return 0; } static int cmd_net_ip6_route_del(const struct shell *sh, size_t argc, char *argv[]) { #if defined(CONFIG_NET_NATIVE_IPV6) && (CONFIG_NET_ROUTE) struct net_if *iface = NULL; int idx; struct net_route_entry *route; struct in6_addr prefix = { 0 }; if (argc != 3) { PR_ERROR("Correct usage: net route del \n"); return -EINVAL; } idx = get_iface_idx(sh, 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_addr_pton(AF_INET6, argv[2], &prefix)) { PR_ERROR("Invalid address: %s\n", argv[2]); return -EINVAL; } route = net_route_lookup(iface, &prefix); if (route) { net_route_del(route); } #else /* CONFIG_NET_NATIVE_IPV6 && CONFIG_NET_ROUTE */ PR_INFO("Set %s and %s to enable native %s support." " And enable CONFIG_NET_ROUTE\n", "CONFIG_NET_NATIVE", "CONFIG_NET_IPV6", "IPv6"); #endif /* CONFIG_NET_NATIVE_IPV6 && CONFIG_NET_ROUTE */ return 0; } static int cmd_net_route(const struct shell *sh, 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.sh = sh; #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_route_mcast_foreach(route_mcast_cb, NULL, &user_data); #endif #endif return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_route, SHELL_CMD(add, NULL, "'net route add '" " adds the route to the destination.", cmd_net_ip6_route_add), SHELL_CMD(del, NULL, "'net route del '" " deletes the route to the destination.", cmd_net_ip6_route_del), SHELL_SUBCMD_SET_END ); SHELL_SUBCMD_ADD((net), route, &net_cmd_route, "Show network route.", cmd_net_route, 1, 0);