| /** @file |
| * @brief Route handling. |
| * |
| */ |
| |
| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #if defined(CONFIG_NET_DEBUG_ROUTE) |
| #define SYS_LOG_DOMAIN "net/route" |
| #define NET_LOG_ENABLED 1 |
| #endif |
| |
| #include <kernel.h> |
| #include <limits.h> |
| #include <zephyr/types.h> |
| #include <misc/slist.h> |
| |
| #include <net/net_pkt.h> |
| #include <net/net_core.h> |
| #include <net/net_stats.h> |
| #include <net/net_mgmt.h> |
| #include <net/net_ip.h> |
| |
| #include "net_private.h" |
| #include "ipv6.h" |
| #include "icmpv6.h" |
| #include "nbr.h" |
| #include "route.h" |
| #include "rpl.h" |
| |
| #if !defined(NET_ROUTE_EXTRA_DATA_SIZE) |
| #define NET_ROUTE_EXTRA_DATA_SIZE 0 |
| #endif |
| |
| /* We keep track of the routes in a separate list so that we can remove |
| * the oldest routes (at tail) if needed. |
| */ |
| static sys_slist_t routes; |
| |
| static void net_route_nexthop_remove(struct net_nbr *nbr) |
| { |
| NET_DBG("Nexthop %p removed", nbr); |
| } |
| |
| /* |
| * This pool contains information next hop neighbors. |
| */ |
| NET_NBR_POOL_INIT(net_route_nexthop_pool, |
| CONFIG_NET_MAX_NEXTHOPS, |
| sizeof(struct net_route_nexthop), |
| net_route_nexthop_remove, |
| 0); |
| |
| static inline struct net_route_nexthop *net_nexthop_data(struct net_nbr *nbr) |
| { |
| return (struct net_route_nexthop *)nbr->data; |
| } |
| |
| static inline struct net_nbr *get_nexthop_nbr(struct net_nbr *start, int idx) |
| { |
| NET_ASSERT_INFO(idx < CONFIG_NET_MAX_NEXTHOPS, "idx %d >= max %d", |
| idx, CONFIG_NET_MAX_NEXTHOPS); |
| |
| return (struct net_nbr *)((void *)start + |
| ((sizeof(struct net_nbr) + start->size) * idx)); |
| } |
| |
| static struct net_nbr *get_nexthop_route(void) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_NEXTHOPS; i++) { |
| struct net_nbr *nbr = get_nexthop_nbr( |
| (struct net_nbr *)net_route_nexthop_pool, i); |
| |
| if (!nbr->ref) { |
| nbr->data = nbr->__nbr; |
| |
| nbr->idx = NET_NBR_LLADDR_UNKNOWN; |
| |
| return net_nbr_ref(nbr); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void net_route_entry_remove(struct net_nbr *nbr) |
| { |
| NET_DBG("Route %p removed", nbr); |
| } |
| |
| static void net_route_entries_table_clear(struct net_nbr_table *table) |
| { |
| NET_DBG("Route table %p cleared", table); |
| } |
| |
| /* |
| * This pool contains routing table entries. |
| */ |
| NET_NBR_POOL_INIT(net_route_entries_pool, |
| CONFIG_NET_MAX_ROUTES, |
| sizeof(struct net_route_entry), |
| net_route_entry_remove, |
| NET_ROUTE_EXTRA_DATA_SIZE); |
| |
| NET_NBR_TABLE_INIT(NET_NBR_LOCAL, nbr_routes, net_route_entries_pool, |
| net_route_entries_table_clear); |
| |
| static inline struct net_nbr *get_nbr(int idx) |
| { |
| return &net_route_entries_pool[idx].nbr; |
| } |
| |
| static inline struct net_route_entry *net_route_data(struct net_nbr *nbr) |
| { |
| return (struct net_route_entry *)nbr->data; |
| } |
| |
| struct net_nbr *net_route_get_nbr(struct net_route_entry *route) |
| { |
| int i; |
| |
| NET_ASSERT(route); |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) { |
| struct net_nbr *nbr = get_nbr(i); |
| |
| if (!nbr->ref) { |
| continue; |
| } |
| |
| if (nbr->data == (u8_t *)route) { |
| if (!nbr->ref) { |
| return NULL; |
| } |
| |
| return nbr; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| #if defined(CONFIG_NET_DEBUG_ROUTE) |
| void net_routes_print(void) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) { |
| struct net_nbr *nbr = get_nbr(i); |
| |
| if (!nbr->ref) { |
| continue; |
| } |
| |
| NET_DBG("[%d] %p %d addr %s/%d iface %p idx %d " |
| "ll %s", |
| i, nbr, nbr->ref, |
| net_sprint_ipv6_addr(&net_route_data(nbr)->addr), |
| net_route_data(nbr)->prefix_len, |
| nbr->iface, nbr->idx, |
| nbr->idx == NET_NBR_LLADDR_UNKNOWN ? "?" : |
| net_sprint_ll_addr( |
| net_nbr_get_lladdr(nbr->idx)->addr, |
| net_nbr_get_lladdr(nbr->idx)->len)); |
| } |
| } |
| #else |
| #define net_routes_print(...) |
| #endif /* CONFIG_NET_DEBUG_ROUTE */ |
| |
| static inline void nbr_free(struct net_nbr *nbr) |
| { |
| NET_DBG("nbr %p", nbr); |
| |
| net_nbr_unref(nbr); |
| } |
| |
| static struct net_nbr *nbr_new(struct net_if *iface, |
| struct in6_addr *addr, |
| u8_t prefix_len) |
| { |
| struct net_nbr *nbr = net_nbr_get(&net_nbr_routes.table); |
| |
| if (!nbr) { |
| return NULL; |
| } |
| |
| nbr->iface = iface; |
| |
| net_ipaddr_copy(&net_route_data(nbr)->addr, addr); |
| net_route_data(nbr)->prefix_len = prefix_len; |
| |
| NET_DBG("[%d] nbr %p iface %p IPv6 %s/%d", |
| nbr->idx, nbr, iface, |
| net_sprint_ipv6_addr(&net_route_data(nbr)->addr), |
| prefix_len); |
| |
| return nbr; |
| } |
| |
| static struct net_nbr *nbr_nexthop_get(struct net_if *iface, |
| struct in6_addr *addr) |
| { |
| /* Note that the nexthop host must be already in the neighbor |
| * cache. We just increase the ref count of an existing entry. |
| */ |
| struct net_nbr *nbr = net_ipv6_nbr_lookup(iface, addr); |
| |
| NET_ASSERT_INFO(nbr, "Next hop neighbor not found!"); |
| NET_ASSERT_INFO(nbr->idx != NET_NBR_LLADDR_UNKNOWN, |
| "Nexthop %s not in neighbor cache!", |
| net_sprint_ipv6_addr(addr)); |
| |
| net_nbr_ref(nbr); |
| |
| NET_DBG("[%d] nbr %p iface %p IPv6 %s", |
| nbr->idx, nbr, iface, net_sprint_ipv6_addr(addr)); |
| |
| return nbr; |
| } |
| |
| static int nbr_nexthop_put(struct net_nbr *nbr) |
| { |
| NET_ASSERT(nbr); |
| |
| NET_DBG("[%d] nbr %p iface %p", nbr->idx, nbr, nbr->iface); |
| |
| net_nbr_unref(nbr); |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_NET_DEBUG_ROUTE) |
| #define net_route_info(str, route, dst) \ |
| do { \ |
| char out[NET_IPV6_ADDR_LEN]; \ |
| struct in6_addr *naddr = net_route_get_nexthop(route); \ |
| \ |
| NET_ASSERT_INFO(naddr, "Unknown nexthop address"); \ |
| \ |
| snprintk(out, sizeof(out), "%s", \ |
| net_sprint_ipv6_addr(dst)); \ |
| NET_DBG("%s route to %s via %s (iface %p)", str, out, \ |
| net_sprint_ipv6_addr(naddr), route->iface); \ |
| } while (0) |
| #else |
| #define net_route_info(...) |
| #endif /* CONFIG_NET_DEBUG_ROUTE */ |
| |
| /* Route was accessed, so place it in front of the routes list */ |
| static inline void update_route_access(struct net_route_entry *route) |
| { |
| sys_slist_find_and_remove(&routes, &route->node); |
| sys_slist_prepend(&routes, &route->node); |
| } |
| |
| struct net_route_entry *net_route_lookup(struct net_if *iface, |
| struct in6_addr *dst) |
| { |
| struct net_route_entry *route, *found = NULL; |
| u8_t longest_match = 0; |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES && longest_match < 128; i++) { |
| struct net_nbr *nbr = get_nbr(i); |
| |
| if (!nbr->ref) { |
| continue; |
| } |
| |
| if (iface && nbr->iface != iface) { |
| continue; |
| } |
| |
| route = net_route_data(nbr); |
| |
| if (route->prefix_len >= longest_match && |
| net_is_ipv6_prefix((u8_t *)dst, |
| (u8_t *)&route->addr, |
| route->prefix_len)) { |
| found = route; |
| longest_match = route->prefix_len; |
| } |
| } |
| |
| if (found) { |
| net_route_info("Found", found, dst); |
| |
| update_route_access(found); |
| } |
| |
| return found; |
| } |
| |
| struct net_route_entry *net_route_add(struct net_if *iface, |
| struct in6_addr *addr, |
| u8_t prefix_len, |
| struct in6_addr *nexthop) |
| { |
| struct net_linkaddr_storage *nexthop_lladdr; |
| struct net_nbr *nbr, *nbr_nexthop, *tmp; |
| struct net_route_nexthop *nexthop_route; |
| struct net_route_entry *route; |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| struct net_event_ipv6_route info; |
| #endif |
| |
| NET_ASSERT(addr); |
| NET_ASSERT(iface); |
| NET_ASSERT(nexthop); |
| |
| if (net_ipv6_addr_cmp(addr, net_ipv6_unspecified_address())) { |
| NET_DBG("Route cannot be towards unspecified address"); |
| return NULL; |
| } |
| |
| nbr_nexthop = net_ipv6_nbr_lookup(iface, nexthop); |
| if (!nbr_nexthop) { |
| NET_DBG("No such neighbor %s found", |
| net_sprint_ipv6_addr(nexthop)); |
| return NULL; |
| } |
| |
| nexthop_lladdr = net_nbr_get_lladdr(nbr_nexthop->idx); |
| |
| NET_ASSERT(nexthop_lladdr); |
| |
| NET_DBG("Nexthop %s lladdr is %s", net_sprint_ipv6_addr(nexthop), |
| net_sprint_ll_addr(nexthop_lladdr->addr, nexthop_lladdr->len)); |
| |
| route = net_route_lookup(iface, addr); |
| if (route) { |
| /* Update nexthop if not the same */ |
| struct in6_addr *nexthop_addr; |
| |
| nexthop_addr = net_route_get_nexthop(route); |
| if (nexthop_addr && net_ipv6_addr_cmp(nexthop, nexthop_addr)) { |
| NET_DBG("No changes, return old route %p", route); |
| return route; |
| } |
| |
| NET_DBG("Old route to %s found", |
| net_sprint_ipv6_addr(nexthop_addr)); |
| |
| net_route_del(route); |
| } |
| |
| nbr = nbr_new(iface, addr, prefix_len); |
| if (!nbr) { |
| /* Remove the oldest route and try again */ |
| sys_snode_t *last = sys_slist_peek_tail(&routes); |
| |
| sys_slist_find_and_remove(&routes, last); |
| |
| route = CONTAINER_OF(last, |
| struct net_route_entry, |
| node); |
| #if defined(CONFIG_NET_DEBUG_ROUTE) |
| do { |
| char out[NET_IPV6_ADDR_LEN]; |
| struct in6_addr *tmp; |
| struct net_linkaddr_storage *llstorage; |
| |
| snprintk(out, sizeof(out), "%s", |
| net_sprint_ipv6_addr(&route->addr)); |
| |
| tmp = net_route_get_nexthop(route); |
| nbr = net_ipv6_nbr_lookup(iface, tmp); |
| llstorage = net_nbr_get_lladdr(nbr->idx); |
| |
| NET_DBG("Removing the oldest route %s via %s [%s]", |
| out, net_sprint_ipv6_addr(tmp), |
| net_sprint_ll_addr(llstorage->addr, |
| llstorage->len)); |
| } while (0); |
| #endif /* CONFIG_NET_DEBUG_ROUTE */ |
| |
| net_route_del(route); |
| |
| nbr = nbr_new(iface, addr, prefix_len); |
| if (!nbr) { |
| NET_ERR("Neighbor route alloc failed!"); |
| return NULL; |
| } |
| } |
| |
| tmp = get_nexthop_route(); |
| if (!tmp) { |
| NET_ERR("No nexthop route available!"); |
| return NULL; |
| } |
| |
| nexthop_route = net_nexthop_data(tmp); |
| |
| route = net_route_data(nbr); |
| route->iface = iface; |
| |
| sys_slist_prepend(&routes, &route->node); |
| |
| tmp = nbr_nexthop_get(iface, nexthop); |
| |
| NET_ASSERT(tmp == nbr_nexthop); |
| |
| nexthop_route->nbr = tmp; |
| |
| sys_slist_init(&route->nexthop); |
| sys_slist_prepend(&route->nexthop, &nexthop_route->node); |
| |
| net_route_info("Added", route, addr); |
| |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| net_ipaddr_copy(&info.addr, addr); |
| net_ipaddr_copy(&info.nexthop, nexthop); |
| info.prefix_len = prefix_len; |
| |
| net_mgmt_event_notify_with_info(NET_EVENT_IPV6_ROUTE_ADD, |
| iface, (void *) &info, |
| sizeof(struct net_event_ipv6_route)); |
| #else |
| net_mgmt_event_notify(NET_EVENT_IPV6_ROUTE_ADD, iface); |
| #endif |
| |
| return route; |
| } |
| |
| int net_route_del(struct net_route_entry *route) |
| { |
| struct net_nbr *nbr; |
| struct net_route_nexthop *nexthop_route; |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| struct net_event_ipv6_route info; |
| #endif |
| |
| if (!route) { |
| return -EINVAL; |
| } |
| |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| net_ipaddr_copy(&info.addr, &route->addr); |
| info.prefix_len = route->prefix_len; |
| net_ipaddr_copy(&info.nexthop, |
| net_route_get_nexthop(route)); |
| |
| net_mgmt_event_notify_with_info(NET_EVENT_IPV6_ROUTE_DEL, |
| route->iface, (void *) &info, |
| sizeof(struct net_event_ipv6_route)); |
| #else |
| net_mgmt_event_notify(NET_EVENT_IPV6_ROUTE_DEL, route->iface); |
| #endif |
| |
| sys_slist_find_and_remove(&routes, &route->node); |
| |
| nbr = net_route_get_nbr(route); |
| if (!nbr) { |
| return -ENOENT; |
| } |
| |
| net_route_info("Deleted", route, &route->addr); |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, node) { |
| if (!nexthop_route->nbr) { |
| continue; |
| } |
| |
| nbr_nexthop_put(nexthop_route->nbr); |
| } |
| |
| nbr_free(nbr); |
| |
| return 0; |
| } |
| |
| int net_route_del_by_nexthop(struct net_if *iface, struct in6_addr *nexthop) |
| { |
| int count = 0, status = 0; |
| struct net_nbr *nbr_nexthop; |
| struct net_route_nexthop *nexthop_route; |
| int i, ret; |
| |
| NET_ASSERT(iface); |
| NET_ASSERT(nexthop); |
| |
| nbr_nexthop = net_ipv6_nbr_lookup(iface, nexthop); |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) { |
| struct net_nbr *nbr = get_nbr(i); |
| struct net_route_entry *route = net_route_data(nbr); |
| |
| if (!route) { |
| continue; |
| } |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, |
| node) { |
| if (nexthop_route->nbr == nbr_nexthop) { |
| /* This route contains this nexthop */ |
| ret = net_route_del(route); |
| if (!ret) { |
| count++; |
| } else { |
| status = ret; |
| } |
| break; |
| } |
| } |
| } |
| |
| if (count) { |
| return count; |
| } else if (status < 0) { |
| return status; |
| } |
| |
| return 0; |
| } |
| |
| int net_route_del_by_nexthop_data(struct net_if *iface, |
| struct in6_addr *nexthop, |
| void *data) |
| { |
| int count = 0, status = 0; |
| struct net_nbr *nbr_nexthop; |
| struct net_route_nexthop *nexthop_route; |
| int i, ret; |
| |
| NET_ASSERT(iface); |
| NET_ASSERT(nexthop); |
| |
| nbr_nexthop = net_ipv6_nbr_lookup(iface, nexthop); |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) { |
| struct net_nbr *nbr = get_nbr(i); |
| struct net_route_entry *route = net_route_data(nbr); |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, |
| node) { |
| void *extra_data; |
| |
| if (nexthop_route->nbr != nbr_nexthop) { |
| continue; |
| } |
| |
| if (nbr->extra_data_size == 0) { |
| continue; |
| } |
| |
| /* Routing engine specific extra data needs |
| * to match too. |
| */ |
| extra_data = net_nbr_extra_data(nbr_nexthop); |
| if (extra_data != data) { |
| continue; |
| } |
| |
| ret = net_route_del(route); |
| if (!ret) { |
| count++; |
| } else { |
| status = ret; |
| } |
| |
| break; |
| } |
| } |
| |
| if (count) { |
| return count; |
| } |
| |
| return status; |
| } |
| |
| struct in6_addr *net_route_get_nexthop(struct net_route_entry *route) |
| { |
| struct net_route_nexthop *nexthop_route; |
| struct net_ipv6_nbr_data *ipv6_nbr_data; |
| |
| if (!route) { |
| return NULL; |
| } |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&route->nexthop, nexthop_route, node) { |
| struct in6_addr *addr; |
| |
| NET_ASSERT(nexthop_route->nbr->idx != NET_NBR_LLADDR_UNKNOWN); |
| |
| if (nexthop_route->nbr->idx == NET_NBR_LLADDR_UNKNOWN) { |
| continue; |
| } |
| |
| ipv6_nbr_data = net_ipv6_nbr_data(nexthop_route->nbr); |
| NET_ASSERT(ipv6_nbr_data); |
| |
| addr = &ipv6_nbr_data->addr; |
| NET_ASSERT(addr); |
| |
| return addr; |
| } |
| |
| return NULL; |
| } |
| |
| int net_route_foreach(net_route_cb_t cb, void *user_data) |
| { |
| int i, ret = 0; |
| |
| for (i = 0; i < CONFIG_NET_MAX_ROUTES; i++) { |
| struct net_route_entry *route; |
| struct net_nbr *nbr; |
| |
| nbr = get_nbr(i); |
| if (!nbr) { |
| continue; |
| } |
| |
| route = net_route_data(nbr); |
| if (!route) { |
| continue; |
| } |
| |
| cb(route, user_data); |
| |
| ret++; |
| } |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_NET_ROUTE_MCAST) |
| /* |
| * This array contains multicast routing entries. |
| */ |
| static |
| struct net_route_entry_mcast route_mcast_entries[CONFIG_NET_MAX_MCAST_ROUTES]; |
| |
| int net_route_mcast_foreach(net_route_mcast_cb_t cb, |
| struct in6_addr *skip, |
| void *user_data) |
| { |
| int i, ret = 0; |
| |
| for (i = 0; i < CONFIG_NET_MAX_MCAST_ROUTES; i++) { |
| struct net_route_entry_mcast *route = &route_mcast_entries[i]; |
| |
| if (route->is_used) { |
| if (skip && net_ipv6_addr_cmp(skip, &route->group)) { |
| continue; |
| } |
| |
| cb(route, user_data); |
| |
| ret++; |
| } |
| } |
| |
| return ret; |
| } |
| |
| struct net_route_entry_mcast *net_route_mcast_add(struct net_if *iface, |
| struct in6_addr *group) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_MCAST_ROUTES; i++) { |
| struct net_route_entry_mcast *route = &route_mcast_entries[i]; |
| |
| if (!route->is_used) { |
| net_ipaddr_copy(&route->group, group); |
| |
| route->iface = iface; |
| route->is_used = true; |
| |
| return route; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| bool net_route_mcast_del(struct net_route_entry_mcast *route) |
| { |
| if (route > &route_mcast_entries[CONFIG_NET_MAX_MCAST_ROUTES - 1] || |
| route < &route_mcast_entries[0]) { |
| return false; |
| } |
| |
| NET_ASSERT_INFO(route->is_used, |
| "Multicast route %p to %s was already removed", route, |
| net_sprint_ipv6_addr(&route->group)); |
| |
| route->is_used = false; |
| |
| return true; |
| } |
| |
| struct net_route_entry_mcast * |
| net_route_mcast_lookup(struct in6_addr *group) |
| { |
| int i; |
| |
| for (i = 0; i < CONFIG_NET_MAX_MCAST_ROUTES; i++) { |
| struct net_route_entry_mcast *route = &route_mcast_entries[i]; |
| |
| if (!route->is_used) { |
| if (net_ipv6_addr_cmp(group, &route->group)) { |
| return route; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| #endif /* CONFIG_NET_ROUTE_MCAST */ |
| |
| bool net_route_get_info(struct net_if *iface, |
| struct in6_addr *dst, |
| struct net_route_entry **route, |
| struct in6_addr **nexthop) |
| { |
| struct net_if_router *router; |
| |
| /* Search in neighbor table first, if not search in routing table. */ |
| if (net_ipv6_nbr_lookup(iface, dst)) { |
| /* Found nexthop, no need to look into routing table. */ |
| *route = NULL; |
| *nexthop = dst; |
| |
| return true; |
| } |
| |
| *route = net_route_lookup(iface, dst); |
| if (*route) { |
| *nexthop = net_route_get_nexthop(*route); |
| if (!*nexthop) { |
| return false; |
| } |
| |
| return true; |
| } else { |
| /* No specific route to this host, use the default |
| * route instead. |
| */ |
| router = net_if_ipv6_router_find_default(NULL, dst); |
| if (!router) { |
| return false; |
| } |
| |
| *nexthop = &router->address.in6_addr; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int net_route_packet(struct net_pkt *pkt, struct in6_addr *nexthop) |
| { |
| struct net_linkaddr_storage *lladdr; |
| struct net_nbr *nbr; |
| |
| nbr = net_ipv6_nbr_lookup(NULL, nexthop); |
| if (!nbr) { |
| NET_DBG("Cannot find %s neighbor", |
| net_sprint_ipv6_addr(nexthop)); |
| return -ENOENT; |
| } |
| |
| lladdr = net_nbr_get_lladdr(nbr->idx); |
| if (!lladdr) { |
| NET_DBG("Cannot find %s neighbor link layer address.", |
| net_sprint_ipv6_addr(nexthop)); |
| return -ESRCH; |
| } |
| |
| if (!net_pkt_ll_src(pkt)->addr) { |
| NET_DBG("Link layer source address not set"); |
| return -EINVAL; |
| } |
| |
| /* Sanitycheck: If src and dst ll addresses are going to be same, |
| * then something went wrong in route lookup. |
| */ |
| if (!memcmp(net_pkt_ll_src(pkt)->addr, lladdr->addr, lladdr->len)) { |
| NET_ERR("Src ll and Dst ll are same"); |
| return -EINVAL; |
| } |
| |
| net_pkt_set_forwarding(pkt, true); |
| |
| /* Set the destination and source ll address in the packet. |
| * We set the destination address to be the nexthop recipient. |
| */ |
| net_pkt_ll_src(pkt)->addr = net_pkt_ll_if(pkt)->addr; |
| net_pkt_ll_src(pkt)->type = net_pkt_ll_if(pkt)->type; |
| net_pkt_ll_src(pkt)->len = net_pkt_ll_if(pkt)->len; |
| |
| net_pkt_ll_dst(pkt)->addr = lladdr->addr; |
| net_pkt_ll_dst(pkt)->type = lladdr->type; |
| net_pkt_ll_dst(pkt)->len = lladdr->len; |
| |
| net_pkt_set_iface(pkt, nbr->iface); |
| |
| return net_send_data(pkt); |
| } |
| |
| void net_route_init(void) |
| { |
| NET_DBG("Allocated %d routing entries (%zu bytes)", |
| CONFIG_NET_MAX_ROUTES, sizeof(net_route_entries_pool)); |
| |
| NET_DBG("Allocated %d nexthop entries (%zu bytes)", |
| CONFIG_NET_MAX_NEXTHOPS, sizeof(net_route_nexthop_pool)); |
| } |