2018-08-15 12:58:57 +02:00
|
|
|
/** @file
|
|
|
|
* @brief IPv6 Neighbor related functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* By default this prints too much data, set the value to 1 to see
|
|
|
|
* neighbor cache contents.
|
|
|
|
*/
|
|
|
|
#define NET_DEBUG_NBR 0
|
|
|
|
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/logging/log.h>
|
2024-03-19 16:23:50 +01:00
|
|
|
LOG_MODULE_REGISTER(net_ipv6_nd, CONFIG_NET_IPV6_ND_LOG_LEVEL);
|
2018-11-30 11:54:56 +01:00
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
2022-05-06 11:12:04 +02:00
|
|
|
#include <zephyr/net/net_core.h>
|
|
|
|
#include <zephyr/net/net_pkt.h>
|
|
|
|
#include <zephyr/net/net_stats.h>
|
|
|
|
#include <zephyr/net/net_context.h>
|
|
|
|
#include <zephyr/net/net_mgmt.h>
|
2023-02-20 08:26:15 +01:00
|
|
|
#include <zephyr/net/dns_resolve.h>
|
2023-09-21 16:24:35 +02:00
|
|
|
#include <zephyr/net/icmp.h>
|
2018-08-15 12:58:57 +02:00
|
|
|
#include "net_private.h"
|
|
|
|
#include "connection.h"
|
|
|
|
#include "icmpv6.h"
|
|
|
|
#include "udp_internal.h"
|
|
|
|
#include "tcp_internal.h"
|
|
|
|
#include "ipv6.h"
|
|
|
|
#include "nbr.h"
|
|
|
|
#include "6lo.h"
|
|
|
|
#include "route.h"
|
|
|
|
#include "net_stats.h"
|
|
|
|
|
|
|
|
/* Timeout value to be used when allocating net buffer during various
|
|
|
|
* neighbor discovery procedures.
|
|
|
|
*/
|
|
|
|
#define ND_NET_BUF_TIMEOUT K_MSEC(100)
|
|
|
|
|
|
|
|
/* Timeout for various buffer allocations in this file. */
|
|
|
|
#define NET_BUF_TIMEOUT K_MSEC(50)
|
|
|
|
|
|
|
|
/* Maximum reachable time value specified in RFC 4861 section
|
|
|
|
* 6.2.1. Router Configuration Variables, AdvReachableTime
|
|
|
|
*/
|
|
|
|
#define MAX_REACHABLE_TIME 3600000
|
|
|
|
|
|
|
|
/* IPv6 minimum link MTU specified in RFC 8200 section 5
|
|
|
|
* Packet Size Issues
|
|
|
|
*/
|
|
|
|
#define MIN_IPV6_MTU NET_IPV6_MTU
|
|
|
|
#define MAX_IPV6_MTU 0xffff
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE) || defined(CONFIG_NET_IPV6_ND)
|
|
|
|
/* Global stale counter, whenever ipv6 neighbor enters into
|
|
|
|
* stale state, stale counter is incremented by one.
|
|
|
|
* When network stack tries to add new neighbor and if table
|
|
|
|
* is full, oldest (oldest stale counter) neighbor in stale
|
|
|
|
* state will be removed from the table and new entry will be
|
|
|
|
* added.
|
|
|
|
*/
|
2020-05-27 18:26:57 +02:00
|
|
|
static uint32_t stale_counter;
|
2019-03-15 09:35:28 +01:00
|
|
|
#endif
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
2021-03-31 17:31:30 +02:00
|
|
|
static struct k_work_delayable ipv6_nd_reachable_timer;
|
2018-08-15 12:58:57 +02:00
|
|
|
static void ipv6_nd_reachable_timeout(struct k_work *work);
|
2020-05-27 18:26:57 +02:00
|
|
|
static void ipv6_nd_restart_reachable_timer(struct net_nbr *nbr, int64_t time);
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
/* Protocol constants from RFC 4861 Chapter 10 */
|
2018-08-15 12:58:57 +02:00
|
|
|
#define MAX_MULTICAST_SOLICIT 3
|
|
|
|
#define MAX_UNICAST_SOLICIT 3
|
2020-04-03 12:04:45 +02:00
|
|
|
#define DELAY_FIRST_PROBE_TIME (5 * MSEC_PER_SEC)
|
|
|
|
#define RETRANS_TIMER 1000 /* ms */
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2023-11-20 18:54:23 +01:00
|
|
|
extern void net_neighbor_remove(struct net_nbr *nbr);
|
2018-08-15 12:58:57 +02:00
|
|
|
extern void net_neighbor_table_clear(struct net_nbr_table *table);
|
|
|
|
|
|
|
|
/** Neighbor Solicitation reply timer */
|
2021-03-31 17:31:30 +02:00
|
|
|
static struct k_work_delayable ipv6_ns_reply_timer;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
NET_NBR_POOL_INIT(net_neighbor_pool,
|
|
|
|
CONFIG_NET_IPV6_MAX_NEIGHBORS,
|
|
|
|
sizeof(struct net_ipv6_nbr_data),
|
2023-11-20 18:54:23 +01:00
|
|
|
net_neighbor_remove);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
NET_NBR_TABLE_INIT(NET_NBR_GLOBAL,
|
|
|
|
neighbor,
|
|
|
|
net_neighbor_pool,
|
|
|
|
net_neighbor_table_clear);
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
static K_MUTEX_DEFINE(nbr_lock);
|
|
|
|
|
|
|
|
void net_ipv6_nbr_lock(void)
|
|
|
|
{
|
|
|
|
(void)k_mutex_lock(&nbr_lock, K_FOREVER);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_ipv6_nbr_unlock(void)
|
|
|
|
{
|
|
|
|
k_mutex_unlock(&nbr_lock);
|
|
|
|
}
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
const char *net_ipv6_nbr_state2str(enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case NET_IPV6_NBR_STATE_INCOMPLETE:
|
|
|
|
return "incomplete";
|
|
|
|
case NET_IPV6_NBR_STATE_REACHABLE:
|
|
|
|
return "reachable";
|
|
|
|
case NET_IPV6_NBR_STATE_STALE:
|
|
|
|
return "stale";
|
|
|
|
case NET_IPV6_NBR_STATE_DELAY:
|
|
|
|
return "delay";
|
|
|
|
case NET_IPV6_NBR_STATE_PROBE:
|
|
|
|
return "probe";
|
|
|
|
case NET_IPV6_NBR_STATE_STATIC:
|
|
|
|
return "static";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "<invalid state>";
|
|
|
|
}
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
static inline struct net_nbr *get_nbr(int idx)
|
|
|
|
{
|
|
|
|
return &net_neighbor_pool[idx].nbr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct net_nbr *get_nbr_from_data(struct net_ipv6_nbr_data *data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
if (nbr->data == (uint8_t *)data) {
|
2019-03-15 09:35:28 +01:00
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
static void ipv6_nbr_set_state(struct net_nbr *nbr,
|
|
|
|
enum net_ipv6_nbr_state new_state)
|
|
|
|
{
|
|
|
|
if (new_state == net_ipv6_nbr_data(nbr)->state ||
|
|
|
|
net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_STATIC) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("nbr %p %s -> %s", nbr,
|
|
|
|
net_ipv6_nbr_state2str(net_ipv6_nbr_data(nbr)->state),
|
|
|
|
net_ipv6_nbr_state2str(new_state));
|
|
|
|
|
|
|
|
net_ipv6_nbr_data(nbr)->state = new_state;
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_STALE) {
|
|
|
|
if (stale_counter + 1 != UINT32_MAX) {
|
|
|
|
net_ipv6_nbr_data(nbr)->stale_counter = stale_counter++;
|
|
|
|
} else {
|
|
|
|
/* Global stale counter reached UINT32_MAX, reset it and
|
|
|
|
* respective neighbors stale counter too.
|
|
|
|
*/
|
|
|
|
struct net_nbr *n = NULL;
|
|
|
|
struct net_ipv6_nbr_data *data = NULL;
|
|
|
|
int i;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
stale_counter = 0U;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
n = get_nbr(i);
|
|
|
|
if (!n || !n->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = net_ipv6_nbr_data(nbr);
|
|
|
|
if (!data) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->state != NET_IPV6_NBR_STATE_STALE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->stale_counter = stale_counter++;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct iface_cb_data {
|
|
|
|
net_nbr_cb_t cb;
|
|
|
|
void *user_data;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void iface_cb(struct net_if *iface, void *user_data)
|
|
|
|
{
|
|
|
|
struct iface_cb_data *data = user_data;
|
|
|
|
int i;
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (!nbr->ref || nbr->iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->cb(nbr, data->user_data);
|
|
|
|
}
|
2024-02-08 16:24:12 +01:00
|
|
|
|
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void net_ipv6_nbr_foreach(net_nbr_cb_t cb, void *user_data)
|
|
|
|
{
|
|
|
|
struct iface_cb_data cb_data = {
|
|
|
|
.cb = cb,
|
|
|
|
.user_data = user_data,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Return the neighbors according to network interface. This makes it
|
|
|
|
* easier in the callback to use the neighbor information.
|
|
|
|
*/
|
|
|
|
net_if_foreach(iface_cb, &cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if NET_DEBUG_NBR
|
|
|
|
void nbr_print(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (!nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("[%d] %p %d/%d/%d/%d/%d pending %p iface %p/%d "
|
2018-08-15 12:58:57 +02:00
|
|
|
"ll %s addr %s",
|
|
|
|
i, nbr, nbr->ref, net_ipv6_nbr_data(nbr)->ns_count,
|
|
|
|
net_ipv6_nbr_data(nbr)->is_router,
|
|
|
|
net_ipv6_nbr_data(nbr)->state,
|
|
|
|
net_ipv6_nbr_data(nbr)->link_metric,
|
|
|
|
net_ipv6_nbr_data(nbr)->pending,
|
|
|
|
nbr->iface, nbr->idx,
|
|
|
|
nbr->idx == NET_NBR_LLADDR_UNKNOWN ? "?" :
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ll_addr(
|
2018-08-15 12:58:57 +02:00
|
|
|
net_nbr_get_lladdr(nbr->idx)->addr,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_nbr_get_lladdr(nbr->idx)->len),
|
|
|
|
net_sprint_ipv6_addr(&net_ipv6_nbr_data(nbr)->addr));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define nbr_print(...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static struct net_nbr *nbr_lookup(struct net_nbr_table *table,
|
|
|
|
struct net_if *iface,
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *addr)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (!nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iface && nbr->iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_ipv6_addr_cmp(&net_ipv6_nbr_data(nbr)->addr, addr)) {
|
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void nbr_clear_ns_pending(struct net_ipv6_nbr_data *data)
|
|
|
|
{
|
|
|
|
data->send_ns = 0;
|
|
|
|
|
|
|
|
if (data->pending) {
|
|
|
|
net_pkt_unref(data->pending);
|
|
|
|
data->pending = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void nbr_free(struct net_nbr *nbr)
|
|
|
|
{
|
|
|
|
NET_DBG("nbr %p", nbr);
|
|
|
|
|
|
|
|
nbr_clear_ns_pending(net_ipv6_nbr_data(nbr));
|
|
|
|
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = 0;
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = 0;
|
|
|
|
|
|
|
|
net_nbr_unref(nbr);
|
|
|
|
net_nbr_unlink(nbr, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool net_ipv6_nbr_rm(struct net_if *iface, struct in6_addr *addr)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr;
|
|
|
|
#if defined(CONFIG_NET_MGMT_EVENT_INFO)
|
|
|
|
struct net_event_ipv6_nbr info;
|
|
|
|
#endif
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
nbr = nbr_lookup(&net_neighbor.table, iface, addr);
|
|
|
|
if (!nbr) {
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove any routes with nbr as nexthop in first place */
|
|
|
|
net_route_del_by_nexthop(iface, addr);
|
|
|
|
|
|
|
|
nbr_free(nbr);
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_MGMT_EVENT_INFO)
|
|
|
|
info.idx = -1;
|
|
|
|
net_ipaddr_copy(&info.addr, addr);
|
|
|
|
net_mgmt_event_notify_with_info(NET_EVENT_IPV6_NBR_DEL,
|
|
|
|
iface, (void *) &info,
|
|
|
|
sizeof(struct net_event_ipv6_nbr));
|
|
|
|
#else
|
|
|
|
net_mgmt_event_notify(NET_EVENT_IPV6_NBR_DEL, iface);
|
|
|
|
#endif
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-03 12:04:45 +02:00
|
|
|
#define NS_REPLY_TIMEOUT (1 * MSEC_PER_SEC)
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
static void ipv6_ns_reply_timeout(struct k_work *work)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
int64_t current = k_uptime_get();
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *nbr = NULL;
|
|
|
|
struct net_ipv6_nbr_data *data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ARG_UNUSED(work);
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
2020-05-27 18:26:57 +02:00
|
|
|
int64_t remaining;
|
2018-08-15 12:58:57 +02:00
|
|
|
nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (!nbr || !nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = net_ipv6_nbr_data(nbr);
|
|
|
|
if (!data) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->send_ns) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
remaining = data->send_ns + NS_REPLY_TIMEOUT - current;
|
|
|
|
|
|
|
|
if (remaining > 0) {
|
2021-03-31 17:31:30 +02:00
|
|
|
if (!k_work_delayable_remaining_get(
|
|
|
|
&ipv6_ns_reply_timer)) {
|
|
|
|
k_work_reschedule(&ipv6_ns_reply_timer,
|
|
|
|
K_MSEC(remaining));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->send_ns = 0;
|
|
|
|
|
|
|
|
/* We did not receive reply to a sent NS */
|
|
|
|
if (!data->pending) {
|
|
|
|
/* Silently return, this is not an error as the work
|
|
|
|
* cannot be cancelled in certain cases.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("NS nbr %p pending %p timeout to %s", nbr,
|
|
|
|
data->pending,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&NET_IPV6_HDR(data->pending)->dst));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* To unref when pending variable was set */
|
|
|
|
net_pkt_unref(data->pending);
|
|
|
|
|
|
|
|
/* To unref the original pkt allocation */
|
|
|
|
net_pkt_unref(data->pending);
|
|
|
|
|
|
|
|
data->pending = NULL;
|
|
|
|
|
|
|
|
net_nbr_unref(nbr);
|
|
|
|
}
|
2024-02-08 16:24:12 +01:00
|
|
|
|
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nbr_init(struct net_nbr *nbr, struct net_if *iface,
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *addr, bool is_router,
|
2018-08-15 12:58:57 +02:00
|
|
|
enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
nbr->idx = NET_NBR_LLADDR_UNKNOWN;
|
|
|
|
nbr->iface = iface;
|
|
|
|
|
|
|
|
net_ipaddr_copy(&net_ipv6_nbr_data(nbr)->addr, addr);
|
|
|
|
ipv6_nbr_set_state(nbr, state);
|
|
|
|
net_ipv6_nbr_data(nbr)->is_router = is_router;
|
|
|
|
net_ipv6_nbr_data(nbr)->pending = NULL;
|
|
|
|
net_ipv6_nbr_data(nbr)->send_ns = 0;
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = 0;
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_nbr *nbr_new(struct net_if *iface,
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *addr, bool is_router,
|
2018-08-15 12:58:57 +02:00
|
|
|
enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr = net_nbr_get(&net_neighbor.table);
|
|
|
|
|
|
|
|
if (!nbr) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
nbr_init(nbr, iface, addr, is_router, state);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("nbr %p iface %p/%d state %d IPv6 %s",
|
|
|
|
nbr, iface, net_if_get_by_iface(iface), state,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(addr));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
|
2020-01-28 17:28:10 +01:00
|
|
|
static void dbg_update_neighbor_lladdr(const struct net_linkaddr *new_lladdr,
|
|
|
|
const struct net_linkaddr_storage *old_lladdr,
|
|
|
|
const struct in6_addr *addr)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
char out[sizeof("xx:xx:xx:xx:xx:xx:xx:xx")];
|
|
|
|
|
|
|
|
snprintk(out, sizeof(out), "%s",
|
|
|
|
net_sprint_ll_addr(old_lladdr->addr, old_lladdr->len));
|
|
|
|
|
|
|
|
NET_DBG("Updating neighbor %s lladdr %s (was %s)",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(addr),
|
|
|
|
net_sprint_ll_addr(new_lladdr->addr, new_lladdr->len),
|
|
|
|
out);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static void dbg_update_neighbor_lladdr_raw(uint8_t *new_lladdr,
|
2018-07-06 10:35:07 +02:00
|
|
|
struct net_linkaddr_storage *old_lladdr,
|
|
|
|
struct in6_addr *addr)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
struct net_linkaddr lladdr = {
|
|
|
|
.len = old_lladdr->len,
|
|
|
|
.addr = new_lladdr,
|
|
|
|
};
|
|
|
|
|
|
|
|
dbg_update_neighbor_lladdr(&lladdr, old_lladdr, addr);
|
|
|
|
}
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr(action, pkt_str, src, dst, pkt) \
|
2018-08-15 12:58:57 +02:00
|
|
|
do { \
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("%s %s from %s to %s iface %p/%d", \
|
|
|
|
action, pkt_str, \
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(src), \
|
|
|
|
net_sprint_ipv6_addr(dst), \
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt), \
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt))); \
|
2018-08-15 12:58:57 +02:00
|
|
|
} while (0)
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr_recv(pkt_str, src, dst, pkt) \
|
|
|
|
dbg_addr("Received", pkt_str, src, dst, pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr_sent(pkt_str, src, dst, pkt) \
|
|
|
|
dbg_addr("Sent", pkt_str, src, dst, pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr_with_tgt(action, pkt_str, src, dst, target, pkt) \
|
2018-08-15 12:58:57 +02:00
|
|
|
do { \
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("%s %s from %s to %s, target %s iface %p/%d", \
|
|
|
|
action, \
|
2018-08-15 12:58:57 +02:00
|
|
|
pkt_str, \
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(src), \
|
|
|
|
net_sprint_ipv6_addr(dst), \
|
|
|
|
net_sprint_ipv6_addr(target), \
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt), \
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt))); \
|
2018-08-15 12:58:57 +02:00
|
|
|
} while (0)
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr_recv_tgt(pkt_str, src, dst, tgt, pkt) \
|
|
|
|
dbg_addr_with_tgt("Received", pkt_str, src, dst, tgt, pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
#define dbg_addr_sent_tgt(pkt_str, src, dst, tgt, pkt) \
|
|
|
|
dbg_addr_with_tgt("Sent", pkt_str, src, dst, tgt, pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
static void ipv6_nd_remove_old_stale_nbr(void)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr = NULL;
|
|
|
|
struct net_ipv6_nbr_data *data = NULL;
|
|
|
|
int nbr_idx = -1;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t oldest = UINT32_MAX;
|
2019-03-15 09:35:28 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
nbr = get_nbr(i);
|
|
|
|
if (!nbr || !nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = net_ipv6_nbr_data(nbr);
|
|
|
|
if (!data || data->is_router ||
|
|
|
|
data->state != NET_IPV6_NBR_STATE_STALE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbr_idx == -1) {
|
|
|
|
nbr_idx = i;
|
|
|
|
oldest = data->stale_counter;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldest == MIN(oldest, data->stale_counter)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbr_idx = i;
|
|
|
|
oldest = data->stale_counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbr_idx != -1) {
|
|
|
|
nbr = get_nbr(nbr_idx);
|
|
|
|
if (!nbr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_ipv6_nbr_rm(nbr->iface,
|
|
|
|
&net_ipv6_nbr_data(nbr)->addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_nbr *add_nbr(struct net_if *iface,
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *addr,
|
2019-03-15 09:35:28 +01:00
|
|
|
bool is_router,
|
|
|
|
enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr;
|
|
|
|
|
|
|
|
nbr = nbr_lookup(&net_neighbor.table, iface, addr);
|
|
|
|
if (nbr) {
|
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbr = nbr_new(iface, addr, is_router, state);
|
|
|
|
if (nbr) {
|
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if there are any stale neighbors, delete the oldest
|
|
|
|
* one and try to add new neighbor.
|
|
|
|
*/
|
|
|
|
ipv6_nd_remove_old_stale_nbr();
|
|
|
|
|
|
|
|
nbr = nbr_new(iface, addr, is_router, state);
|
|
|
|
if (!nbr) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *net_ipv6_nbr_add(struct net_if *iface,
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *addr,
|
|
|
|
const struct net_linkaddr *lladdr,
|
2018-08-15 12:58:57 +02:00
|
|
|
bool is_router,
|
|
|
|
enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr;
|
|
|
|
int ret;
|
|
|
|
#if defined(CONFIG_NET_MGMT_EVENT_INFO)
|
|
|
|
struct net_event_ipv6_nbr info;
|
|
|
|
#endif
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
nbr = add_nbr(iface, addr, is_router, state);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (!nbr) {
|
2019-03-15 09:35:28 +01:00
|
|
|
NET_ERR("Could not add router neighbor %s [%s]",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(addr),
|
2022-09-28 12:18:26 +02:00
|
|
|
lladdr ? net_sprint_ll_addr(lladdr->addr, lladdr->len) : "unknown");
|
2024-02-08 16:24:12 +01:00
|
|
|
goto out;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2022-09-28 12:18:26 +02:00
|
|
|
if (lladdr && net_nbr_link(nbr, iface, lladdr) == -EALREADY &&
|
2018-08-15 12:58:57 +02:00
|
|
|
net_ipv6_nbr_data(nbr)->state != NET_IPV6_NBR_STATE_STATIC) {
|
|
|
|
/* Update the lladdr if the node was already known */
|
|
|
|
struct net_linkaddr_storage *cached_lladdr;
|
|
|
|
|
|
|
|
cached_lladdr = net_nbr_get_lladdr(nbr->idx);
|
|
|
|
|
|
|
|
if (memcmp(cached_lladdr->addr, lladdr->addr, lladdr->len)) {
|
|
|
|
dbg_update_neighbor_lladdr(lladdr, cached_lladdr, addr);
|
|
|
|
|
|
|
|
net_linkaddr_set(cached_lladdr, lladdr->addr,
|
|
|
|
lladdr->len);
|
|
|
|
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
|
|
|
|
} else if (net_ipv6_nbr_data(nbr)->state ==
|
|
|
|
NET_IPV6_NBR_STATE_INCOMPLETE) {
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_INCOMPLETE) {
|
|
|
|
/* Send NS so that we can verify that the neighbor is
|
|
|
|
* reachable.
|
|
|
|
*/
|
|
|
|
ret = net_ipv6_send_ns(iface, NULL, NULL, NULL, addr, false);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Cannot send NS (%d)", ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("[%d] nbr %p state %d router %d IPv6 %s ll %s iface %p/%d",
|
2018-08-15 12:58:57 +02:00
|
|
|
nbr->idx, nbr, state, is_router,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(addr),
|
2022-09-28 12:18:26 +02:00
|
|
|
lladdr ? net_sprint_ll_addr(lladdr->addr, lladdr->len) : "[unknown]",
|
2020-02-14 20:19:07 +01:00
|
|
|
nbr->iface, net_if_get_by_iface(nbr->iface));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_MGMT_EVENT_INFO)
|
|
|
|
info.idx = nbr->idx;
|
|
|
|
net_ipaddr_copy(&info.addr, addr);
|
|
|
|
net_mgmt_event_notify_with_info(NET_EVENT_IPV6_NBR_ADD,
|
|
|
|
iface, (void *) &info,
|
|
|
|
sizeof(struct net_event_ipv6_nbr));
|
|
|
|
#else
|
|
|
|
net_mgmt_event_notify(NET_EVENT_IPV6_NBR_ADD, iface);
|
|
|
|
#endif
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
out:
|
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return nbr;
|
|
|
|
}
|
|
|
|
|
2023-11-20 18:54:23 +01:00
|
|
|
void net_neighbor_remove(struct net_nbr *nbr)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
NET_DBG("Neighbor %p removed", nbr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_neighbor_table_clear(struct net_nbr_table *table)
|
|
|
|
{
|
|
|
|
NET_DBG("Neighbor table %p cleared", table);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct in6_addr *net_ipv6_nbr_lookup_by_index(struct net_if *iface,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t idx)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (!nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iface && nbr->iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbr->idx == idx) {
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return &net_ipv6_nbr_data(nbr)->addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
const char *net_ipv6_nbr_state2str(enum net_ipv6_nbr_state state)
|
|
|
|
{
|
|
|
|
return "<unknown state>";
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_NBR_CACHE */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_DAD)
|
|
|
|
int net_ipv6_start_dad(struct net_if *iface, struct net_if_addr *ifaddr)
|
|
|
|
{
|
|
|
|
return net_ipv6_send_ns(iface, NULL, NULL, NULL,
|
|
|
|
&ifaddr->address.in6_addr, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool dad_failed(struct net_if *iface, struct in6_addr *addr)
|
|
|
|
{
|
2018-11-02 15:05:58 +01:00
|
|
|
if (net_ipv6_is_ll_addr(addr)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
NET_ERR("DAD failed, no ll IPv6 address!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_if_ipv6_dad_failed(iface, addr);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_DAD */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
|
|
|
|
static struct in6_addr *check_route(struct net_if *iface,
|
|
|
|
struct in6_addr *dst,
|
|
|
|
bool *try_route)
|
|
|
|
{
|
|
|
|
struct in6_addr *nexthop = NULL;
|
|
|
|
struct net_route_entry *route;
|
|
|
|
struct net_if_router *router;
|
|
|
|
|
|
|
|
route = net_route_lookup(iface, dst);
|
|
|
|
if (route) {
|
|
|
|
nexthop = net_route_get_nexthop(route);
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Route %p nexthop %s iface %p/%d",
|
|
|
|
route,
|
2022-06-20 07:43:37 +02:00
|
|
|
nexthop ? net_sprint_ipv6_addr(nexthop) :
|
2020-02-14 20:19:07 +01:00
|
|
|
"<unknown>",
|
|
|
|
iface, net_if_get_by_iface(iface));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (!nexthop) {
|
|
|
|
net_route_del(route);
|
|
|
|
|
|
|
|
NET_DBG("No route to host %s",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(dst));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No specific route to this host, use the default
|
|
|
|
* route instead.
|
|
|
|
*/
|
|
|
|
router = net_if_ipv6_router_find_default(NULL, dst);
|
|
|
|
if (!router) {
|
|
|
|
NET_DBG("No default route to %s",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(dst));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* Try to send the packet anyway */
|
|
|
|
nexthop = dst;
|
|
|
|
if (try_route) {
|
|
|
|
*try_route = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nexthop;
|
|
|
|
}
|
|
|
|
|
|
|
|
nexthop = &router->address.in6_addr;
|
|
|
|
|
|
|
|
NET_DBG("Router %p nexthop %s", router,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(nexthop));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nexthop;
|
|
|
|
}
|
|
|
|
|
2018-12-21 09:08:19 +01:00
|
|
|
enum net_verdict net_ipv6_prepare_for_send(struct net_pkt *pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-21 09:08:19 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
|
2018-08-15 12:58:57 +02:00
|
|
|
struct in6_addr *nexthop = NULL;
|
|
|
|
struct net_if *iface = NULL;
|
2018-12-21 09:08:19 +01:00
|
|
|
struct net_ipv6_hdr *ip_hdr;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *nbr;
|
|
|
|
int ret;
|
|
|
|
|
2018-12-21 09:08:19 +01:00
|
|
|
NET_ASSERT(pkt && pkt->buffer);
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
ip_hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ipv6_access);
|
2018-12-21 09:08:19 +01:00
|
|
|
if (!ip_hdr) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
|
|
/* If we have already fragmented the packet, the fragment id will
|
|
|
|
* contain a proper value and we can skip other checks.
|
|
|
|
*/
|
2019-03-27 02:57:45 +01:00
|
|
|
if (net_pkt_ipv6_fragment_id(pkt) == 0U) {
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t mtu = net_if_get_mtu(net_pkt_iface(pkt));
|
2018-08-15 12:58:57 +02:00
|
|
|
size_t pkt_len = net_pkt_get_len(pkt);
|
|
|
|
|
2019-03-21 14:29:11 +01:00
|
|
|
mtu = MAX(NET_IPV6_MTU, mtu);
|
|
|
|
if (mtu < pkt_len) {
|
2018-08-15 12:58:57 +02:00
|
|
|
ret = net_ipv6_send_fragmented_pkt(net_pkt_iface(pkt),
|
|
|
|
pkt, pkt_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Cannot fragment IPv6 pkt (%d)", ret);
|
|
|
|
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
/* Try to send the packet if we could
|
|
|
|
* not allocate enough network packets
|
|
|
|
* and hope the original large packet
|
|
|
|
* can be sent ok.
|
|
|
|
*/
|
|
|
|
goto ignore_frag_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We need to unref here because we simulate the packet
|
|
|
|
* sending.
|
|
|
|
*/
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
|
|
|
|
/* No need to continue with the sending as the packet
|
|
|
|
* is now split and its fragments will be sent
|
|
|
|
* separately to network.
|
|
|
|
*/
|
2018-12-21 09:08:19 +01:00
|
|
|
return NET_CONTINUE;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ignore_frag_error:
|
|
|
|
#endif /* CONFIG_NET_IPV6_FRAGMENT */
|
|
|
|
|
|
|
|
/* If the IPv6 destination address is not link local, then try to get
|
|
|
|
* the next hop from routing table if we have multi interface routing
|
|
|
|
* enabled. The reason for this is that the neighbor cache will not
|
|
|
|
* contain public IPv6 address information so in that case we should
|
|
|
|
* not enter this branch.
|
|
|
|
*/
|
2018-09-11 09:16:03 +02:00
|
|
|
if ((net_pkt_lladdr_dst(pkt)->addr &&
|
2018-08-15 12:58:57 +02:00
|
|
|
((IS_ENABLED(CONFIG_NET_ROUTING) &&
|
2021-10-05 12:23:57 +02:00
|
|
|
net_ipv6_is_ll_addr((struct in6_addr *)ip_hdr->dst)) ||
|
2018-08-15 12:58:57 +02:00
|
|
|
!IS_ENABLED(CONFIG_NET_ROUTING))) ||
|
2021-10-05 12:23:57 +02:00
|
|
|
net_ipv6_is_addr_mcast((struct in6_addr *)ip_hdr->dst) ||
|
2018-12-21 09:08:19 +01:00
|
|
|
/* Workaround Linux bug, see:
|
|
|
|
* https://github.com/zephyrproject-rtos/zephyr/issues/3111
|
|
|
|
*/
|
2023-02-26 15:01:47 +01:00
|
|
|
net_if_flag_is_set(net_pkt_iface(pkt), NET_IF_POINTOPOINT) ||
|
|
|
|
net_if_flag_is_set(net_pkt_iface(pkt), NET_IF_IPV6_NO_ND)) {
|
2018-12-21 09:08:19 +01:00
|
|
|
return NET_OK;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_if_ipv6_addr_onlink(&iface, (struct in6_addr *)ip_hdr->dst)) {
|
|
|
|
nexthop = (struct in6_addr *)ip_hdr->dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_set_iface(pkt, iface);
|
2024-03-15 06:46:14 +01:00
|
|
|
} else if (net_ipv6_is_ll_addr((struct in6_addr *)ip_hdr->dst)) {
|
|
|
|
nexthop = (struct in6_addr *)ip_hdr->dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
|
|
|
/* We need to figure out where the destination
|
|
|
|
* host is located.
|
|
|
|
*/
|
|
|
|
bool try_route = false;
|
|
|
|
|
2021-10-05 12:23:57 +02:00
|
|
|
nexthop = check_route(NULL, (struct in6_addr *)ip_hdr->dst,
|
|
|
|
&try_route);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (!nexthop) {
|
2018-12-21 09:08:19 +01:00
|
|
|
return NET_DROP;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (try_route) {
|
|
|
|
goto try_send;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!iface) {
|
|
|
|
/* This means that the dst was not onlink, so try to
|
|
|
|
* figure out the interface using nexthop instead.
|
|
|
|
*/
|
|
|
|
if (net_if_ipv6_addr_onlink(&iface, nexthop)) {
|
|
|
|
net_pkt_set_iface(pkt, iface);
|
|
|
|
} else {
|
2020-06-05 12:32:24 +02:00
|
|
|
/* nexthop might be the nbr list, e.g. a link-local
|
|
|
|
* address of a connected peer.
|
|
|
|
*/
|
|
|
|
nbr = net_ipv6_nbr_lookup(NULL, nexthop);
|
|
|
|
if (nbr) {
|
|
|
|
iface = nbr->iface;
|
|
|
|
net_pkt_set_iface(pkt, iface);
|
|
|
|
} else {
|
|
|
|
iface = net_pkt_iface(pkt);
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the above check returns null, we try to send
|
|
|
|
* the packet and hope for the best.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
try_send:
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
nbr = nbr_lookup(&net_neighbor.table, iface, nexthop);
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Neighbor lookup %p (%d) iface %p/%d addr %s state %s", nbr,
|
|
|
|
nbr ? nbr->idx : NET_NBR_LLADDR_UNKNOWN,
|
|
|
|
iface, net_if_get_by_iface(iface),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(nexthop),
|
2018-08-15 12:58:57 +02:00
|
|
|
nbr ? net_ipv6_nbr_state2str(net_ipv6_nbr_data(nbr)->state) :
|
|
|
|
"-");
|
|
|
|
|
|
|
|
if (nbr && nbr->idx != NET_NBR_LLADDR_UNKNOWN) {
|
|
|
|
struct net_linkaddr_storage *lladdr;
|
|
|
|
|
|
|
|
lladdr = net_nbr_get_lladdr(nbr->idx);
|
|
|
|
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_dst(pkt)->addr = lladdr->addr;
|
|
|
|
net_pkt_lladdr_dst(pkt)->len = lladdr->len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
NET_DBG("Neighbor %p addr %s", nbr,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ll_addr(lladdr->addr, lladdr->len));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* Start the NUD if we are in STALE state.
|
|
|
|
* See RFC 4861 ch 7.3.3 for details.
|
|
|
|
*/
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
|
|
if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_STALE) {
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_DELAY);
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
ipv6_nd_restart_reachable_timer(nbr,
|
|
|
|
DELAY_FIRST_PROBE_TIME);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-12-21 09:08:19 +01:00
|
|
|
return NET_OK;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
|
|
/* We need to send NS and wait for NA before sending the packet. */
|
|
|
|
ret = net_ipv6_send_ns(net_pkt_iface(pkt), pkt,
|
2021-10-05 12:23:57 +02:00
|
|
|
(struct in6_addr *)ip_hdr->src, NULL, nexthop,
|
|
|
|
false);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
/* In case of an error, the NS send function will unref
|
|
|
|
* the pkt.
|
|
|
|
*/
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Cannot send NS (%d) iface %p/%d",
|
|
|
|
ret, net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("pkt %p (buffer %p) will be sent later to iface %p/%d",
|
|
|
|
pkt, pkt->buffer, net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-12-21 09:08:19 +01:00
|
|
|
|
|
|
|
return NET_CONTINUE;
|
2018-08-15 12:58:57 +02:00
|
|
|
#else
|
|
|
|
ARG_UNUSED(ret);
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("pkt %p (buffer %p) cannot be sent to iface %p/%d, "
|
|
|
|
"dropping it.", pkt, pkt->buffer,
|
|
|
|
net_pkt_iface(pkt), net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-21 09:08:19 +01:00
|
|
|
return NET_DROP;
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif /* CONFIG_NET_IPV6_ND */
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_nbr *net_ipv6_nbr_lookup(struct net_if *iface,
|
|
|
|
struct in6_addr *addr)
|
|
|
|
{
|
2024-02-08 16:24:12 +01:00
|
|
|
struct net_nbr *nbr;
|
|
|
|
|
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
nbr = nbr_lookup(&net_neighbor.table, iface, addr);
|
|
|
|
net_ipv6_nbr_unlock();
|
|
|
|
|
|
|
|
return nbr;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
struct net_nbr *net_ipv6_get_nbr(struct net_if *iface, uint8_t idx)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2024-02-08 16:24:12 +01:00
|
|
|
struct net_nbr *ret = NULL;
|
2018-08-15 12:58:57 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
|
|
|
struct net_nbr *nbr = get_nbr(i);
|
|
|
|
|
|
|
|
if (nbr->ref) {
|
|
|
|
if (iface && nbr->iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbr->idx == idx) {
|
2024-02-08 16:24:12 +01:00
|
|
|
ret = nbr;
|
|
|
|
break;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
|
|
|
return ret;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline uint8_t get_llao_len(struct net_if *iface)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t total_len = net_if_get_link_addr(iface)->len +
|
2019-08-09 10:06:24 +02:00
|
|
|
sizeof(struct net_icmpv6_nd_opt_hdr);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-08-09 10:06:24 +02:00
|
|
|
return ROUND_UP(total_len, 8U);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
static inline bool set_llao(struct net_pkt *pkt,
|
|
|
|
struct net_linkaddr *lladdr,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t llao_len, uint8_t type)
|
2018-12-20 10:33:35 +01:00
|
|
|
{
|
|
|
|
struct net_icmpv6_nd_opt_hdr opt_hdr = {
|
|
|
|
.type = type,
|
|
|
|
.len = llao_len >> 3,
|
|
|
|
};
|
|
|
|
|
2019-02-20 10:01:57 +01:00
|
|
|
if (net_pkt_write(pkt, &opt_hdr,
|
|
|
|
sizeof(struct net_icmpv6_nd_opt_hdr)) ||
|
|
|
|
net_pkt_write(pkt, lladdr->addr, lladdr->len) ||
|
2018-12-20 10:33:35 +01:00
|
|
|
net_pkt_memset(pkt, 0, llao_len - lladdr->len - 2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
static bool read_llao(struct net_pkt *pkt,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t len,
|
2020-01-16 19:56:24 +01:00
|
|
|
struct net_linkaddr_storage *llstorage)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t padding;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
llstorage->len = NET_LINK_ADDR_MAX_LENGTH;
|
|
|
|
if (net_pkt_lladdr_src(pkt)->len < llstorage->len) {
|
|
|
|
llstorage->len = net_pkt_lladdr_src(pkt)->len;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
if (net_pkt_read(pkt, llstorage->addr, llstorage->len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
padding = len * 8U - 2 - llstorage->len;
|
|
|
|
if (padding) {
|
|
|
|
if (net_pkt_skip(pkt, padding)) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
int net_ipv6_send_na(struct net_if *iface, const struct in6_addr *src,
|
|
|
|
const struct in6_addr *dst, const struct in6_addr *tgt,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t flags)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 10:33:35 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(na_access,
|
|
|
|
struct net_icmpv6_na_hdr);
|
|
|
|
int ret = -ENOBUFS;
|
|
|
|
struct net_icmpv6_na_hdr *na_hdr;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_pkt *pkt;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t llao_len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
llao_len = get_llao_len(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
pkt = net_pkt_alloc_with_buffer(iface,
|
|
|
|
sizeof(struct net_icmpv6_na_hdr) +
|
|
|
|
llao_len,
|
|
|
|
AF_INET6, IPPROTO_ICMPV6,
|
|
|
|
ND_NET_BUF_TIMEOUT);
|
|
|
|
if (!pkt) {
|
2018-08-15 12:58:57 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-02-20 08:58:08 +01:00
|
|
|
if (net_ipv6_create(pkt, src, dst) ||
|
2018-12-20 10:33:35 +01:00
|
|
|
net_icmpv6_create(pkt, NET_ICMPV6_NA, 0)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
na_hdr = (struct net_icmpv6_na_hdr *)net_pkt_get_data(pkt, &na_access);
|
2018-12-20 10:33:35 +01:00
|
|
|
if (!na_hdr) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
/* Let's make sure reserved part is full of 0 */
|
|
|
|
memset(na_hdr, 0, sizeof(struct net_icmpv6_na_hdr));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
na_hdr->flags = flags;
|
2021-10-06 10:06:26 +02:00
|
|
|
net_ipv6_addr_copy_raw(na_hdr->tgt, (uint8_t *)tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
if (net_pkt_set_data(pkt, &na_access)) {
|
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
if (!set_llao(pkt, net_if_get_link_addr(iface),
|
|
|
|
llao_len, NET_ICMPV6_ND_OPT_TLLAO)) {
|
2018-12-20 10:33:35 +01:00
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
2019-02-01 21:29:39 +01:00
|
|
|
net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
|
2018-12-20 10:33:35 +01:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
dbg_addr_sent_tgt("Neighbor Advertisement", src, dst, &na_hdr->tgt,
|
|
|
|
pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (net_send_data(pkt) < 0) {
|
2018-12-20 10:33:35 +01:00
|
|
|
net_stats_update_ipv6_nd_drop(iface);
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2023-02-01 15:31:36 +01:00
|
|
|
net_stats_update_icmp_sent(net_pkt_iface(pkt));
|
2018-12-20 10:33:35 +01:00
|
|
|
net_stats_update_ipv6_nd_sent(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
drop:
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
|
2018-12-20 10:33:35 +01:00
|
|
|
return ret;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ns_routing_info(struct net_pkt *pkt,
|
|
|
|
struct in6_addr *nexthop,
|
|
|
|
struct in6_addr *tgt)
|
|
|
|
{
|
2018-11-30 11:54:56 +01:00
|
|
|
if (CONFIG_NET_IPV6_LOG_LEVEL >= LOG_LEVEL_DBG) {
|
2018-07-06 10:35:07 +02:00
|
|
|
char out[NET_IPV6_ADDR_LEN];
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-07-06 10:35:07 +02:00
|
|
|
snprintk(out, sizeof(out), "%s",
|
|
|
|
net_sprint_ipv6_addr(nexthop));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-07-06 10:35:07 +02:00
|
|
|
if (net_ipv6_addr_cmp(nexthop, tgt)) {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Routing to %s iface %p/%d",
|
2022-06-20 07:43:37 +02:00
|
|
|
out,
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-07-06 10:35:07 +02:00
|
|
|
} else {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Routing to %s via %s iface %p/%d",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(tgt),
|
|
|
|
out,
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-07-06 10:35:07 +02:00
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
static int handle_ns_input(struct net_icmp_ctx *ctx,
|
|
|
|
struct net_pkt *pkt,
|
|
|
|
struct net_icmp_ip_hdr *hdr,
|
|
|
|
struct net_icmp_hdr *icmp_hdr,
|
|
|
|
void *user_data)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ns_access,
|
|
|
|
struct net_icmpv6_ns_hdr);
|
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr);
|
2023-09-21 16:24:35 +02:00
|
|
|
struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t length = net_pkt_get_len(pkt);
|
|
|
|
uint8_t flags = 0U;
|
2018-08-15 12:58:57 +02:00
|
|
|
bool routing = false;
|
2018-12-20 14:00:05 +01:00
|
|
|
struct net_icmpv6_nd_opt_hdr *nd_opt_hdr;
|
|
|
|
struct net_icmpv6_ns_hdr *ns_hdr;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_if_addr *ifaddr;
|
2020-01-28 17:28:10 +01:00
|
|
|
const struct in6_addr *na_src;
|
|
|
|
const struct in6_addr *na_dst;
|
2018-12-20 14:00:05 +01:00
|
|
|
struct in6_addr *tgt;
|
2020-01-15 18:51:25 +01:00
|
|
|
struct net_linkaddr_storage src_lladdr_s;
|
|
|
|
struct net_linkaddr src_lladdr;
|
|
|
|
|
|
|
|
src_lladdr.len = 0;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2023-02-26 15:01:47 +01:00
|
|
|
if (net_if_flag_is_set(net_pkt_iface(pkt), NET_IF_IPV6_NO_ND)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
ns_hdr = (struct net_icmpv6_ns_hdr *)net_pkt_get_data(pkt, &ns_access);
|
2018-12-20 14:00:05 +01:00
|
|
|
if (!ns_hdr) {
|
|
|
|
NET_ERR("DROP: NULL NS header");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_addr_recv_tgt("Neighbor Solicitation",
|
2020-02-14 20:19:07 +01:00
|
|
|
&ip_hdr->src, &ip_hdr->dst, &ns_hdr->tgt, pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt));
|
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
if (((length < (sizeof(struct net_ipv6_hdr) +
|
2018-08-15 12:58:57 +02:00
|
|
|
sizeof(struct net_icmp_hdr) +
|
|
|
|
sizeof(struct net_icmpv6_ns_hdr))) ||
|
2018-12-20 14:00:05 +01:00
|
|
|
(ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT)) &&
|
2021-10-06 10:06:26 +02:00
|
|
|
(net_ipv6_is_addr_mcast((struct in6_addr *)ns_hdr->tgt) &&
|
|
|
|
icmp_hdr->code != 0U)) {
|
2018-12-20 14:00:05 +01:00
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &ns_access);
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_ns_hdr));
|
2018-12-20 14:00:05 +01:00
|
|
|
length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
while (nd_opt_hdr && nd_opt_hdr->len > 0 &&
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt) < length) {
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t prev_opt_len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
switch (nd_opt_hdr->type) {
|
2018-08-15 12:58:57 +02:00
|
|
|
case NET_ICMPV6_ND_OPT_SLLAO:
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_ipv6_is_addr_unspecified(
|
|
|
|
(struct in6_addr *)ip_hdr->src)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2020-01-15 18:51:25 +01:00
|
|
|
if (!read_llao(pkt, nd_opt_hdr->len, &src_lladdr_s)) {
|
2020-01-28 17:28:10 +01:00
|
|
|
NET_ERR("DROP: failed to read LLAO");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2020-01-15 18:51:25 +01:00
|
|
|
src_lladdr.len = src_lladdr_s.len;
|
|
|
|
src_lladdr.addr = src_lladdr_s.addr;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
break;
|
2018-08-15 12:58:57 +02:00
|
|
|
default:
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type);
|
2018-08-15 12:58:57 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_opt_len = net_pkt_ipv6_ext_opt_len(pkt);
|
|
|
|
|
|
|
|
net_pkt_set_ipv6_ext_opt_len(pkt,
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt) +
|
2018-12-20 14:00:05 +01:00
|
|
|
(nd_opt_hdr->len << 3));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (prev_opt_len >= net_pkt_ipv6_ext_opt_len(pkt)) {
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_ERR("DROP: Corrupted NS message");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING)) {
|
2021-10-06 10:06:26 +02:00
|
|
|
ifaddr = net_if_ipv6_addr_lookup((struct in6_addr *)ns_hdr->tgt,
|
|
|
|
NULL);
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
2021-10-06 10:06:26 +02:00
|
|
|
ifaddr = net_if_ipv6_addr_lookup_by_iface(
|
|
|
|
net_pkt_iface(pkt), (struct in6_addr *)ns_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifaddr) {
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING)) {
|
|
|
|
struct in6_addr *nexthop;
|
|
|
|
|
2021-10-06 10:06:26 +02:00
|
|
|
nexthop = check_route(NULL,
|
|
|
|
(struct in6_addr *)ns_hdr->tgt,
|
|
|
|
NULL);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (nexthop) {
|
2021-10-06 10:06:26 +02:00
|
|
|
ns_routing_info(pkt, nexthop,
|
|
|
|
(struct in6_addr *)ns_hdr->tgt);
|
2021-10-05 12:23:57 +02:00
|
|
|
na_dst = (struct in6_addr *)ip_hdr->dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
/* Note that the target is not the address of
|
|
|
|
* the "nethop" as that is a link-local address
|
|
|
|
* which is not routable.
|
|
|
|
*/
|
2021-10-06 10:06:26 +02:00
|
|
|
tgt = (struct in6_addr *)ns_hdr->tgt;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* Source address must be one of our real
|
|
|
|
* interface address where the packet was
|
|
|
|
* received.
|
|
|
|
*/
|
2020-01-28 17:28:10 +01:00
|
|
|
na_src = net_if_ipv6_select_src_addr(
|
2021-10-05 12:23:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)ip_hdr->src);
|
2020-01-28 17:28:10 +01:00
|
|
|
if (!na_src) {
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: No interface address "
|
2020-02-14 20:19:07 +01:00
|
|
|
"for dst %s iface %p/%d",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ip_hdr->src),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(
|
|
|
|
net_pkt_iface(pkt)));
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
routing = true;
|
|
|
|
goto nexthop_found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: No such interface address %s",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ns_hdr->tgt));
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
} else {
|
|
|
|
tgt = &ifaddr->address.in6_addr;
|
2021-10-05 12:23:57 +02:00
|
|
|
na_src = (struct in6_addr *)ip_hdr->dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
nexthop_found:
|
|
|
|
|
|
|
|
#if !defined(CONFIG_NET_IPV6_DAD)
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_ipv6_is_addr_unspecified((struct in6_addr *)ip_hdr->src)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_NET_IPV6_DAD */
|
|
|
|
|
|
|
|
/* Do DAD */
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_ipv6_is_addr_unspecified((struct in6_addr *)ip_hdr->src)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2021-10-05 12:23:57 +02:00
|
|
|
if (!net_ipv6_is_addr_solicited_node((struct in6_addr *)ip_hdr->dst)) {
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: Not solicited node addr %s",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ip_hdr->dst));
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ifaddr->addr_state == NET_ADDR_TENTATIVE) {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("DROP: DAD failed for %s iface %p/%d",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ifaddr->address.in6_addr),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
dad_failed(net_pkt_iface(pkt),
|
|
|
|
&ifaddr->address.in6_addr);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2020-01-28 17:28:10 +01:00
|
|
|
/* We reuse the received packet for the NA addresses*/
|
2021-10-05 12:23:57 +02:00
|
|
|
net_ipv6_addr_create_ll_allnodes_mcast(
|
|
|
|
(struct in6_addr *)ip_hdr->dst);
|
|
|
|
net_ipaddr_copy((struct in6_addr *)ip_hdr->src,
|
|
|
|
net_if_ipv6_select_src_addr(
|
|
|
|
net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)ip_hdr->dst));
|
|
|
|
|
|
|
|
na_src = (struct in6_addr *)ip_hdr->src;
|
|
|
|
na_dst = (struct in6_addr *)ip_hdr->dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
flags = NET_ICMPV6_NA_FLAG_OVERRIDE;
|
|
|
|
goto send_na;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_DAD */
|
|
|
|
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_ipv6_is_my_addr((struct in6_addr *)ip_hdr->src)) {
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: Duplicate IPv6 %s address",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ip_hdr->src));
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Address resolution */
|
2021-10-05 12:23:57 +02:00
|
|
|
if (net_ipv6_is_addr_solicited_node((struct in6_addr *)ip_hdr->dst)) {
|
2021-10-06 10:06:26 +02:00
|
|
|
na_src = (struct in6_addr *)ns_hdr->tgt;
|
2021-10-05 12:23:57 +02:00
|
|
|
na_dst = (struct in6_addr *)ip_hdr->src;
|
2018-08-15 12:58:57 +02:00
|
|
|
flags = NET_ICMPV6_NA_FLAG_SOLICITED |
|
|
|
|
NET_ICMPV6_NA_FLAG_OVERRIDE;
|
|
|
|
goto send_na;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (routing) {
|
|
|
|
/* No need to do NUD here when the target is being routed. */
|
|
|
|
goto send_na;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Neighbor Unreachability Detection (NUD) */
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING)) {
|
2021-10-05 12:23:57 +02:00
|
|
|
ifaddr = net_if_ipv6_addr_lookup((struct in6_addr *)ip_hdr->dst,
|
|
|
|
NULL);
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
2021-10-05 12:23:57 +02:00
|
|
|
ifaddr = net_if_ipv6_addr_lookup_by_iface(
|
|
|
|
net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)ip_hdr->dst);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ifaddr) {
|
2021-10-06 10:06:26 +02:00
|
|
|
na_src = (struct in6_addr *)ns_hdr->tgt;
|
2021-10-05 12:23:57 +02:00
|
|
|
na_dst = (struct in6_addr *)ip_hdr->src;
|
2018-08-15 12:58:57 +02:00
|
|
|
tgt = &ifaddr->address.in6_addr;
|
|
|
|
flags = NET_ICMPV6_NA_FLAG_SOLICITED |
|
|
|
|
NET_ICMPV6_NA_FLAG_OVERRIDE;
|
|
|
|
goto send_na;
|
|
|
|
} else {
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: NUD failed");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_na:
|
2020-01-15 18:51:25 +01:00
|
|
|
if (src_lladdr.len) {
|
|
|
|
if (!net_ipv6_nbr_add(net_pkt_iface(pkt),
|
2021-10-05 12:23:57 +02:00
|
|
|
(struct in6_addr *)ip_hdr->src,
|
2020-01-15 18:51:25 +01:00
|
|
|
&src_lladdr, false,
|
|
|
|
NET_IPV6_NBR_STATE_INCOMPLETE)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-28 17:28:10 +01:00
|
|
|
if (!net_ipv6_send_na(net_pkt_iface(pkt), na_src,
|
|
|
|
na_dst, tgt, flags)) {
|
2023-09-21 16:24:35 +02:00
|
|
|
return 0;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 14:00:05 +01:00
|
|
|
NET_DBG("DROP: Cannot send NA");
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return -EIO;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
drop:
|
|
|
|
net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt));
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return -EIO;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_NBR_CACHE */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
2020-05-27 18:26:57 +02:00
|
|
|
static void ipv6_nd_restart_reachable_timer(struct net_nbr *nbr, int64_t time)
|
2019-03-15 09:35:28 +01:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
int64_t remaining;
|
2019-03-15 09:35:28 +01:00
|
|
|
|
|
|
|
if (nbr) {
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = k_uptime_get();
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = time;
|
|
|
|
}
|
|
|
|
|
2021-03-31 17:31:30 +02:00
|
|
|
remaining = k_ticks_to_ms_ceil32(
|
|
|
|
k_work_delayable_remaining_get(&ipv6_nd_reachable_timer));
|
2019-03-15 09:35:28 +01:00
|
|
|
if (!remaining || remaining > time) {
|
2021-03-31 17:31:30 +02:00
|
|
|
k_work_reschedule(&ipv6_nd_reachable_timer, K_MSEC(time));
|
2019-03-15 09:35:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
static void ipv6_nd_reachable_timeout(struct k_work *work)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
int64_t current = k_uptime_get();
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *nbr = NULL;
|
|
|
|
struct net_ipv6_nbr_data *data = NULL;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
for (i = 0; i < CONFIG_NET_IPV6_MAX_NEIGHBORS; i++) {
|
2020-05-27 18:26:57 +02:00
|
|
|
int64_t remaining;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
nbr = get_nbr(i);
|
|
|
|
if (!nbr || !nbr->ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = net_ipv6_nbr_data(nbr);
|
|
|
|
if (!data) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->reachable) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
remaining = data->reachable + data->reachable_timeout - current;
|
|
|
|
if (remaining > 0) {
|
2019-03-15 09:35:28 +01:00
|
|
|
ipv6_nd_restart_reachable_timer(NULL, remaining);
|
2018-08-15 12:58:57 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->reachable = 0;
|
|
|
|
|
|
|
|
switch (data->state) {
|
|
|
|
case NET_IPV6_NBR_STATE_STATIC:
|
2020-01-08 08:42:23 +01:00
|
|
|
NET_ASSERT(false, "Static entry shall never timeout");
|
2018-08-15 12:58:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NET_IPV6_NBR_STATE_INCOMPLETE:
|
|
|
|
if (data->ns_count >= MAX_MULTICAST_SOLICIT) {
|
2019-03-15 09:35:28 +01:00
|
|
|
net_ipv6_nbr_rm(nbr->iface, &data->addr);
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
|
|
|
data->ns_count++;
|
|
|
|
|
|
|
|
NET_DBG("nbr %p incomplete count %u", nbr,
|
|
|
|
data->ns_count);
|
|
|
|
|
|
|
|
ret = net_ipv6_send_ns(nbr->iface, NULL, NULL,
|
|
|
|
NULL, &data->addr,
|
|
|
|
false);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Cannot send NS (%d)", ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NET_IPV6_NBR_STATE_REACHABLE:
|
|
|
|
data->state = NET_IPV6_NBR_STATE_STALE;
|
|
|
|
|
|
|
|
NET_DBG("nbr %p moving %s state to STALE (%d)",
|
2018-10-02 13:57:55 +02:00
|
|
|
nbr,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&data->addr),
|
2018-08-15 12:58:57 +02:00
|
|
|
data->state);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NET_IPV6_NBR_STATE_STALE:
|
|
|
|
NET_DBG("nbr %p removing stale address %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
nbr,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&data->addr));
|
2019-03-15 09:35:28 +01:00
|
|
|
net_ipv6_nbr_rm(nbr->iface, &data->addr);
|
2018-08-15 12:58:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NET_IPV6_NBR_STATE_DELAY:
|
|
|
|
data->state = NET_IPV6_NBR_STATE_PROBE;
|
2018-11-29 20:23:03 +01:00
|
|
|
data->ns_count = 0U;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
NET_DBG("nbr %p moving %s state to PROBE (%d)",
|
2018-10-02 13:57:55 +02:00
|
|
|
nbr,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&data->addr),
|
2018-08-15 12:58:57 +02:00
|
|
|
data->state);
|
|
|
|
|
|
|
|
/* Intentionally continuing to probe state */
|
2020-08-21 22:45:52 +02:00
|
|
|
__fallthrough;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
case NET_IPV6_NBR_STATE_PROBE:
|
|
|
|
if (data->ns_count >= MAX_UNICAST_SOLICIT) {
|
2019-03-15 09:35:28 +01:00
|
|
|
net_ipv6_nbr_rm(nbr->iface, &data->addr);
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
|
|
|
data->ns_count++;
|
|
|
|
|
|
|
|
NET_DBG("nbr %p probe count %u", nbr,
|
|
|
|
data->ns_count);
|
|
|
|
|
|
|
|
ret = net_ipv6_send_ns(nbr->iface, NULL, NULL,
|
|
|
|
NULL, &data->addr,
|
|
|
|
false);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Cannot send NS (%d)", ret);
|
|
|
|
}
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
ipv6_nd_restart_reachable_timer(nbr,
|
|
|
|
RETRANS_TIMER);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-02-08 16:24:12 +01:00
|
|
|
|
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void net_ipv6_nbr_set_reachable_timer(struct net_if *iface,
|
|
|
|
struct net_nbr *nbr)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t time;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
time = net_if_ipv6_get_reachable_time(iface);
|
|
|
|
|
2020-01-08 08:42:23 +01:00
|
|
|
NET_ASSERT(time, "Zero reachable timeout!");
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
NET_DBG("Starting reachable timer nbr %p data %p time %d ms",
|
|
|
|
nbr, net_ipv6_nbr_data(nbr), time);
|
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
ipv6_nd_restart_reachable_timer(nbr, time);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
2024-01-28 16:51:38 +01:00
|
|
|
|
|
|
|
void net_ipv6_nbr_reachability_hint(struct net_if *iface,
|
|
|
|
const struct in6_addr *ipv6_addr)
|
|
|
|
{
|
|
|
|
struct net_nbr *nbr = NULL;
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2024-01-28 16:51:38 +01:00
|
|
|
nbr = nbr_lookup(&net_neighbor.table, iface, ipv6_addr);
|
|
|
|
|
|
|
|
NET_DBG("nbr %p got rechability hint", nbr);
|
|
|
|
|
|
|
|
if (nbr && net_ipv6_nbr_data(nbr)->state != NET_IPV6_NBR_STATE_INCOMPLETE &&
|
|
|
|
net_ipv6_nbr_data(nbr)->state != NET_IPV6_NBR_STATE_STATIC) {
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE);
|
|
|
|
|
|
|
|
/* We might have active timer from PROBE */
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = 0;
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = 0;
|
|
|
|
|
|
|
|
net_ipv6_nbr_set_reachable_timer(iface, nbr);
|
|
|
|
}
|
2024-02-08 16:24:12 +01:00
|
|
|
|
|
|
|
net_ipv6_nbr_unlock();
|
2024-01-28 16:51:38 +01:00
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif /* CONFIG_NET_IPV6_ND */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
|
|
|
|
static inline bool handle_na_neighbor(struct net_pkt *pkt,
|
|
|
|
struct net_icmpv6_na_hdr *na_hdr,
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t tllao_offset)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
struct net_linkaddr_storage lladdr = { 0 };
|
2018-12-20 13:30:25 +01:00
|
|
|
bool lladdr_changed = false;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_linkaddr_storage *cached_lladdr;
|
|
|
|
struct net_pkt *pending;
|
2018-12-20 13:30:25 +01:00
|
|
|
struct net_nbr *nbr;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
|
|
|
|
2021-10-06 10:06:26 +02:00
|
|
|
nbr = nbr_lookup(&net_neighbor.table, net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)na_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Neighbor lookup %p iface %p/%d addr %s", nbr,
|
|
|
|
net_pkt_iface(pkt), net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&na_hdr->tgt));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (!nbr) {
|
|
|
|
nbr_print();
|
|
|
|
|
|
|
|
NET_DBG("No such neighbor found, msg discarded");
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tllao_offset) {
|
2019-06-15 14:52:34 +02:00
|
|
|
lladdr.len = net_pkt_lladdr_src(pkt)->len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
|
|
|
|
|
|
|
if (net_pkt_skip(pkt, tllao_offset) ||
|
2019-02-20 09:40:48 +01:00
|
|
|
net_pkt_read(pkt, lladdr.addr, lladdr.len)) {
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nbr->idx == NET_NBR_LLADDR_UNKNOWN) {
|
|
|
|
struct net_linkaddr nbr_lladdr;
|
|
|
|
|
|
|
|
if (!tllao_offset) {
|
|
|
|
NET_DBG("No target link layer address.");
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
nbr_lladdr.len = lladdr.len;
|
|
|
|
nbr_lladdr.addr = lladdr.addr;
|
|
|
|
|
|
|
|
if (net_nbr_link(nbr, net_pkt_iface(pkt), &nbr_lladdr)) {
|
|
|
|
nbr_free(nbr);
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("[%d] nbr %p state %d IPv6 %s ll %s",
|
|
|
|
nbr->idx, nbr, net_ipv6_nbr_data(nbr)->state,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&na_hdr->tgt),
|
|
|
|
net_sprint_ll_addr(nbr_lladdr.addr, nbr_lladdr.len));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cached_lladdr = net_nbr_get_lladdr(nbr->idx);
|
|
|
|
if (!cached_lladdr) {
|
|
|
|
NET_DBG("No lladdr but index defined");
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tllao_offset) {
|
|
|
|
lladdr_changed = memcmp(lladdr.addr,
|
|
|
|
cached_lladdr->addr,
|
|
|
|
cached_lladdr->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the cached address if we do not yet known it */
|
|
|
|
if (net_ipv6_nbr_data(nbr)->state == NET_IPV6_NBR_STATE_INCOMPLETE) {
|
|
|
|
if (!tllao_offset) {
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lladdr_changed) {
|
2021-10-06 10:06:26 +02:00
|
|
|
dbg_update_neighbor_lladdr_raw(
|
|
|
|
lladdr.addr, cached_lladdr,
|
|
|
|
(struct in6_addr *)na_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_linkaddr_set(cached_lladdr, lladdr.addr,
|
|
|
|
cached_lladdr->len);
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED) {
|
2018-08-15 12:58:57 +02:00
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE);
|
2018-11-29 20:23:03 +01:00
|
|
|
net_ipv6_nbr_data(nbr)->ns_count = 0U;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* We might have active timer from PROBE */
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = 0;
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = 0;
|
|
|
|
|
|
|
|
net_ipv6_nbr_set_reachable_timer(net_pkt_iface(pkt),
|
|
|
|
nbr);
|
|
|
|
} else {
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_ipv6_nbr_data(nbr)->is_router =
|
|
|
|
(na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
goto send_pending;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We do not update the address if override bit is not set
|
|
|
|
* and we have a valid address in the cache.
|
|
|
|
*/
|
2018-12-20 13:30:25 +01:00
|
|
|
if (!(na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE) && lladdr_changed) {
|
2018-08-15 12:58:57 +02:00
|
|
|
if (net_ipv6_nbr_data(nbr)->state ==
|
|
|
|
NET_IPV6_NBR_STATE_REACHABLE) {
|
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_STALE);
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
goto err;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE ||
|
|
|
|
(!(na_hdr->flags & NET_ICMPV6_NA_FLAG_OVERRIDE) &&
|
|
|
|
tllao_offset && !lladdr_changed)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (lladdr_changed) {
|
|
|
|
dbg_update_neighbor_lladdr_raw(
|
2021-10-06 10:06:26 +02:00
|
|
|
lladdr.addr, cached_lladdr,
|
|
|
|
(struct in6_addr *)na_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_linkaddr_set(cached_lladdr, lladdr.addr,
|
|
|
|
cached_lladdr->len);
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED) {
|
2018-08-15 12:58:57 +02:00
|
|
|
ipv6_nbr_set_state(nbr, NET_IPV6_NBR_STATE_REACHABLE);
|
|
|
|
|
|
|
|
/* We might have active timer from PROBE */
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable = 0;
|
|
|
|
net_ipv6_nbr_data(nbr)->reachable_timeout = 0;
|
|
|
|
|
|
|
|
net_ipv6_nbr_set_reachable_timer(net_pkt_iface(pkt),
|
|
|
|
nbr);
|
|
|
|
} else {
|
|
|
|
if (lladdr_changed) {
|
|
|
|
ipv6_nbr_set_state(nbr,
|
|
|
|
NET_IPV6_NBR_STATE_STALE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (net_ipv6_nbr_data(nbr)->is_router &&
|
|
|
|
!(na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
/* Update the routing if the peer is no longer
|
|
|
|
* a router.
|
|
|
|
*/
|
|
|
|
/* FIXME */
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_ipv6_nbr_data(nbr)->is_router =
|
|
|
|
(na_hdr->flags & NET_ICMPV6_NA_FLAG_ROUTER);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
send_pending:
|
|
|
|
/* Next send any pending messages to the peer. */
|
|
|
|
pending = net_ipv6_nbr_data(nbr)->pending;
|
|
|
|
if (pending) {
|
2018-12-20 13:30:25 +01:00
|
|
|
NET_DBG("Sending pending %p to lladdr %s", pending,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ll_addr(cached_lladdr->addr, cached_lladdr->len));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (net_send_data(pending) < 0) {
|
|
|
|
nbr_clear_ns_pending(net_ipv6_nbr_data(nbr));
|
|
|
|
} else {
|
|
|
|
net_ipv6_nbr_data(nbr)->pending = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_pkt_unref(pending);
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
return true;
|
2024-02-08 16:24:12 +01:00
|
|
|
|
|
|
|
err:
|
|
|
|
net_ipv6_nbr_unlock();
|
|
|
|
return false;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
static int handle_na_input(struct net_icmp_ctx *ctx,
|
|
|
|
struct net_pkt *pkt,
|
|
|
|
struct net_icmp_ip_hdr *hdr,
|
|
|
|
struct net_icmp_hdr *icmp_hdr,
|
|
|
|
void *user_data)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 13:30:25 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(na_access,
|
|
|
|
struct net_icmpv6_na_hdr);
|
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr);
|
2023-09-21 16:24:35 +02:00
|
|
|
struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t length = net_pkt_get_len(pkt);
|
|
|
|
uint16_t tllao_offset = 0U;
|
2018-12-20 13:30:25 +01:00
|
|
|
struct net_icmpv6_nd_opt_hdr *nd_opt_hdr;
|
|
|
|
struct net_icmpv6_na_hdr *na_hdr;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_if_addr *ifaddr;
|
|
|
|
|
2023-02-26 15:01:47 +01:00
|
|
|
if (net_if_flag_is_set(net_pkt_iface(pkt), NET_IF_IPV6_NO_ND)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
na_hdr = (struct net_icmpv6_na_hdr *)net_pkt_get_data(pkt, &na_access);
|
2018-12-20 13:30:25 +01:00
|
|
|
if (!na_hdr) {
|
|
|
|
NET_ERR("DROP: NULL NA header");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_addr_recv_tgt("Neighbor Advertisement",
|
2020-02-14 20:19:07 +01:00
|
|
|
&ip_hdr->src, &ip_hdr->dst, &na_hdr->tgt, pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt));
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (((length < (sizeof(struct net_ipv6_hdr) +
|
|
|
|
sizeof(struct net_icmp_hdr) +
|
|
|
|
sizeof(struct net_icmpv6_na_hdr) +
|
|
|
|
sizeof(struct net_icmpv6_nd_opt_hdr))) ||
|
|
|
|
(ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT) ||
|
2021-10-06 10:06:26 +02:00
|
|
|
net_ipv6_is_addr_mcast((struct in6_addr *)na_hdr->tgt) ||
|
2018-12-20 13:30:25 +01:00
|
|
|
(na_hdr->flags & NET_ICMPV6_NA_FLAG_SOLICITED &&
|
2021-10-05 12:23:57 +02:00
|
|
|
net_ipv6_is_addr_mcast((struct in6_addr *)ip_hdr->dst))) &&
|
2019-03-27 02:57:45 +01:00
|
|
|
(icmp_hdr->code != 0U)) {
|
2018-12-20 13:30:25 +01:00
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &na_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_na_hdr));
|
|
|
|
length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
while (nd_opt_hdr && nd_opt_hdr->len &&
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt) < length) {
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t prev_opt_len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
switch (nd_opt_hdr->type) {
|
2018-08-15 12:58:57 +02:00
|
|
|
case NET_ICMPV6_ND_OPT_TLLAO:
|
|
|
|
tllao_offset = net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr) +
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt) + 1 + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-12-20 13:30:25 +01:00
|
|
|
NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type);
|
2018-08-15 12:58:57 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_opt_len = net_pkt_ipv6_ext_opt_len(pkt);
|
|
|
|
|
|
|
|
net_pkt_set_ipv6_ext_opt_len(pkt,
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt) +
|
2018-12-20 13:30:25 +01:00
|
|
|
(nd_opt_hdr->len << 3));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (prev_opt_len >= net_pkt_ipv6_ext_opt_len(pkt)) {
|
2018-12-20 13:30:25 +01:00
|
|
|
NET_ERR("DROP: Corrupted NA message");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &nd_access);
|
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ifaddr = net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt),
|
2021-10-06 10:06:26 +02:00
|
|
|
(struct in6_addr *)na_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (ifaddr) {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Interface %p/%d already has address %s",
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&na_hdr->tgt));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_DAD)
|
|
|
|
if (ifaddr->addr_state == NET_ADDR_TENTATIVE) {
|
2021-10-06 10:06:26 +02:00
|
|
|
dad_failed(net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)na_hdr->tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_DAD */
|
|
|
|
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-12-20 13:30:25 +01:00
|
|
|
if (!handle_na_neighbor(pkt, na_hdr, tllao_offset)) {
|
2023-12-04 08:44:49 +01:00
|
|
|
/* Update the statistics but silently drop NA msg if the sender
|
|
|
|
* is not known or if there was an error in the message.
|
|
|
|
* Returning <0 will cause error message to be printed which
|
|
|
|
* is too much for this non error.
|
|
|
|
*/
|
|
|
|
net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return 0;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
drop:
|
|
|
|
net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt));
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return -EIO;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int net_ipv6_send_ns(struct net_if *iface,
|
|
|
|
struct net_pkt *pending,
|
2018-12-20 11:08:14 +01:00
|
|
|
const struct in6_addr *src,
|
|
|
|
const struct in6_addr *dst,
|
|
|
|
const struct in6_addr *tgt,
|
2018-08-15 12:58:57 +02:00
|
|
|
bool is_my_address)
|
|
|
|
{
|
2018-12-20 11:08:14 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ns_access,
|
|
|
|
struct net_icmpv6_ns_hdr);
|
|
|
|
struct net_pkt *pkt = NULL;
|
|
|
|
int ret = -ENOBUFS;
|
|
|
|
struct net_icmpv6_ns_hdr *ns_hdr;
|
|
|
|
struct in6_addr node_dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *nbr;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t llao_len;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
if (!dst) {
|
|
|
|
net_ipv6_addr_create_solicited_node(tgt, &node_dst);
|
|
|
|
dst = &node_dst;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
llao_len = get_llao_len(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
if (is_my_address) {
|
|
|
|
src = net_ipv6_unspecified_address();
|
2019-03-27 02:57:45 +01:00
|
|
|
llao_len = 0U;
|
2018-12-20 11:08:14 +01:00
|
|
|
} else {
|
|
|
|
if (!src) {
|
2020-01-29 10:17:37 +01:00
|
|
|
src = net_if_ipv6_select_src_addr(iface, tgt);
|
2018-12-20 11:08:14 +01:00
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
if (net_ipv6_is_addr_unspecified(src)) {
|
2020-01-29 10:17:37 +01:00
|
|
|
NET_DBG("No source address for NS (tgt %s)",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(tgt));
|
2018-12-20 11:08:14 +01:00
|
|
|
ret = -EINVAL;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
pkt = net_pkt_alloc_with_buffer(iface,
|
|
|
|
sizeof(struct net_icmpv6_ns_hdr) +
|
|
|
|
llao_len,
|
|
|
|
AF_INET6, IPPROTO_ICMPV6,
|
|
|
|
ND_NET_BUF_TIMEOUT);
|
|
|
|
if (!pkt) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2021-03-25 09:15:27 +01:00
|
|
|
/* Avoid recursive loop with network packet capturing */
|
|
|
|
if (IS_ENABLED(CONFIG_NET_CAPTURE) && pending) {
|
|
|
|
net_pkt_set_captured(pkt, net_pkt_is_captured(pending));
|
|
|
|
}
|
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-02-20 08:58:08 +01:00
|
|
|
if (net_ipv6_create(pkt, src, dst) ||
|
2018-12-20 11:08:14 +01:00
|
|
|
net_icmpv6_create(pkt, NET_ICMPV6_NS, 0)) {
|
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
ns_hdr = (struct net_icmpv6_ns_hdr *)net_pkt_get_data(pkt, &ns_access);
|
2018-12-20 11:08:14 +01:00
|
|
|
if (!ns_hdr) {
|
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
ns_hdr->reserved = 0U;
|
2021-10-06 10:06:26 +02:00
|
|
|
net_ipv6_addr_copy_raw(ns_hdr->tgt, (uint8_t *)tgt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
if (net_pkt_set_data(pkt, &ns_access)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
if (!is_my_address) {
|
2018-12-20 11:24:13 +01:00
|
|
|
if (!set_llao(pkt, net_if_get_link_addr(iface),
|
|
|
|
llao_len, NET_ICMPV6_ND_OPT_SLLAO)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
2019-02-01 21:29:39 +01:00
|
|
|
net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
2021-10-06 10:06:26 +02:00
|
|
|
nbr = add_nbr(iface, tgt, false,
|
2019-03-15 09:35:28 +01:00
|
|
|
NET_IPV6_NBR_STATE_INCOMPLETE);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (!nbr) {
|
2019-03-15 09:35:28 +01:00
|
|
|
NET_DBG("Could not create new neighbor %s",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&ns_hdr->tgt));
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2019-03-15 09:35:28 +01:00
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pending) {
|
|
|
|
if (!net_ipv6_nbr_data(nbr)->pending) {
|
|
|
|
net_ipv6_nbr_data(nbr)->pending = net_pkt_ref(pending);
|
|
|
|
} else {
|
|
|
|
NET_DBG("Packet %p already pending for "
|
|
|
|
"operation. Discarding pending %p and pkt %p",
|
|
|
|
net_ipv6_nbr_data(nbr)->pending, pending, pkt);
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("Setting timeout %d for NS", NS_REPLY_TIMEOUT);
|
|
|
|
|
|
|
|
net_ipv6_nbr_data(nbr)->send_ns = k_uptime_get();
|
|
|
|
|
|
|
|
/* Let's start the timer if necessary */
|
2021-03-31 17:31:30 +02:00
|
|
|
if (!k_work_delayable_remaining_get(&ipv6_ns_reply_timer)) {
|
|
|
|
k_work_reschedule(&ipv6_ns_reply_timer,
|
|
|
|
K_MSEC(NS_REPLY_TIMEOUT));
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
dbg_addr_sent_tgt("Neighbor Solicitation", src, dst, &ns_hdr->tgt,
|
|
|
|
pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (net_send_data(pkt) < 0) {
|
|
|
|
NET_DBG("Cannot send NS %p (pending %p)", pkt, pending);
|
|
|
|
|
|
|
|
if (pending) {
|
|
|
|
nbr_clear_ns_pending(net_ipv6_nbr_data(nbr));
|
2018-12-20 11:08:14 +01:00
|
|
|
pending = NULL;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
|
|
|
|
2023-02-01 15:31:36 +01:00
|
|
|
net_stats_update_icmp_sent(net_pkt_iface(pkt));
|
2018-12-20 11:08:14 +01:00
|
|
|
net_stats_update_ipv6_nd_sent(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
drop:
|
2018-12-20 11:08:14 +01:00
|
|
|
if (pending) {
|
|
|
|
net_pkt_unref(pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pkt) {
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 11:08:14 +01:00
|
|
|
net_stats_update_ipv6_nd_drop(iface);
|
|
|
|
|
|
|
|
return ret;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_NBR_CACHE */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
|
|
|
int net_ipv6_send_rs(struct net_if *iface)
|
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint8_t llao_len = 0U;
|
2018-12-20 11:24:13 +01:00
|
|
|
int ret = -ENOBUFS;
|
|
|
|
const struct in6_addr *src;
|
|
|
|
struct in6_addr dst;
|
|
|
|
struct net_pkt *pkt;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-07-11 09:27:29 +02:00
|
|
|
net_ipv6_addr_create_ll_allrouters_mcast(&dst);
|
2018-12-20 11:24:13 +01:00
|
|
|
src = net_if_ipv6_select_src_addr(iface, &dst);
|
|
|
|
|
|
|
|
if (!net_ipv6_is_addr_unspecified(src)) {
|
|
|
|
llao_len = get_llao_len(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
pkt = net_pkt_alloc_with_buffer(iface,
|
|
|
|
sizeof(struct net_icmpv6_rs_hdr) +
|
|
|
|
llao_len,
|
|
|
|
AF_INET6, IPPROTO_ICMPV6,
|
|
|
|
ND_NET_BUF_TIMEOUT);
|
|
|
|
if (!pkt) {
|
2018-08-15 12:58:57 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-02-20 08:58:08 +01:00
|
|
|
if (net_ipv6_create(pkt, src, &dst) ||
|
2018-12-20 11:24:13 +01:00
|
|
|
net_icmpv6_create(pkt, NET_ICMPV6_RS, 0) ||
|
|
|
|
net_pkt_memset(pkt, 0, sizeof(struct net_icmpv6_rs_hdr))) {
|
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
if (llao_len > 0) {
|
|
|
|
if (!set_llao(pkt, net_if_get_link_addr(iface),
|
|
|
|
llao_len, NET_ICMPV6_ND_OPT_SLLAO)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
2019-02-01 21:29:39 +01:00
|
|
|
net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
dbg_addr_sent("Router Solicitation", src, &dst, pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (net_send_data(pkt) < 0) {
|
2018-12-20 11:24:13 +01:00
|
|
|
net_stats_update_ipv6_nd_drop(iface);
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2023-02-01 15:31:36 +01:00
|
|
|
net_stats_update_icmp_sent(net_pkt_iface(pkt));
|
2018-12-20 11:24:13 +01:00
|
|
|
net_stats_update_ipv6_nd_sent(iface);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
drop:
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
|
2018-12-20 11:24:13 +01:00
|
|
|
return ret;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int net_ipv6_start_rs(struct net_if *iface)
|
|
|
|
{
|
|
|
|
return net_ipv6_send_rs(iface);
|
|
|
|
}
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline struct net_nbr *handle_ra_neighbor(struct net_pkt *pkt, uint8_t len)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
|
|
|
struct net_linkaddr lladdr;
|
|
|
|
struct net_linkaddr_storage llstorage;
|
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
if (!read_llao(pkt, len, &llstorage)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-01-16 19:56:24 +01:00
|
|
|
lladdr.len = llstorage.len;
|
|
|
|
lladdr.addr = llstorage.addr;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-03-15 09:35:28 +01:00
|
|
|
return net_ipv6_nbr_add(net_pkt_iface(pkt),
|
2021-10-05 12:23:57 +02:00
|
|
|
(struct in6_addr *)NET_IPV6_HDR(pkt)->src,
|
2019-03-15 09:35:28 +01:00
|
|
|
&lladdr, true,
|
|
|
|
NET_IPV6_NBR_STATE_STALE);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void handle_prefix_onlink(struct net_pkt *pkt,
|
|
|
|
struct net_icmpv6_nd_opt_prefix_info *prefix_info)
|
|
|
|
{
|
|
|
|
struct net_if_ipv6_prefix *prefix;
|
|
|
|
|
|
|
|
prefix = net_if_ipv6_prefix_lookup(net_pkt_iface(pkt),
|
2021-10-06 10:06:26 +02:00
|
|
|
(struct in6_addr *)prefix_info->prefix,
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->prefix_len);
|
|
|
|
if (!prefix) {
|
|
|
|
if (!prefix_info->valid_lifetime) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix = net_if_ipv6_prefix_add(net_pkt_iface(pkt),
|
2021-10-06 10:06:26 +02:00
|
|
|
(struct in6_addr *)prefix_info->prefix,
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->prefix_len,
|
|
|
|
prefix_info->valid_lifetime);
|
|
|
|
if (prefix) {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Interface %p/%d add prefix %s/%d lifetime %u",
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&prefix_info->prefix),
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->prefix_len,
|
|
|
|
prefix_info->valid_lifetime);
|
|
|
|
} else {
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_ERR("Prefix %s/%d could not be added to "
|
|
|
|
"iface %p/%d",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&prefix_info->prefix),
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->prefix_len,
|
2020-02-14 20:19:07 +01:00
|
|
|
net_pkt_iface(pkt),
|
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (prefix_info->valid_lifetime) {
|
|
|
|
case 0:
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Interface %p/%d delete prefix %s/%d",
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&prefix_info->prefix),
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->prefix_len);
|
|
|
|
|
|
|
|
net_if_ipv6_prefix_rm(net_pkt_iface(pkt),
|
|
|
|
&prefix->prefix,
|
|
|
|
prefix->len);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NET_IPV6_ND_INFINITE_LIFETIME:
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Interface %p/%d prefix %s/%d infinite",
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&prefix->prefix),
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix->len);
|
|
|
|
|
|
|
|
net_if_ipv6_prefix_set_lf(prefix, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-02-14 20:19:07 +01:00
|
|
|
NET_DBG("Interface %p/%d update prefix %s/%u lifetime %u",
|
2018-08-15 12:58:57 +02:00
|
|
|
net_pkt_iface(pkt),
|
2020-02-14 20:19:07 +01:00
|
|
|
net_if_get_by_iface(net_pkt_iface(pkt)),
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&prefix_info->prefix),
|
2018-12-20 15:26:22 +01:00
|
|
|
prefix_info->prefix_len, prefix_info->valid_lifetime);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_if_ipv6_prefix_set_lf(prefix, false);
|
|
|
|
net_if_ipv6_prefix_set_timer(prefix,
|
2018-12-20 15:26:22 +01:00
|
|
|
prefix_info->valid_lifetime);
|
2018-08-15 12:58:57 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TWO_HOURS (2 * 60 * 60)
|
|
|
|
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline uint32_t remaining_lifetime(struct net_if_addr *ifaddr)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2020-12-25 20:32:42 +01:00
|
|
|
return net_timeout_remaining(&ifaddr->lifetime, k_uptime_get_32());
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void handle_prefix_autonomous(struct net_pkt *pkt,
|
|
|
|
struct net_icmpv6_nd_opt_prefix_info *prefix_info)
|
|
|
|
{
|
|
|
|
struct in6_addr addr = { };
|
|
|
|
struct net_if_addr *ifaddr;
|
|
|
|
|
|
|
|
/* Create IPv6 address using the given prefix and iid. We first
|
|
|
|
* setup link local address, and then copy prefix over first 8
|
|
|
|
* bytes of that address.
|
|
|
|
*/
|
|
|
|
net_ipv6_addr_create_iid(&addr,
|
|
|
|
net_if_get_link_addr(net_pkt_iface(pkt)));
|
2021-10-06 10:06:26 +02:00
|
|
|
memcpy(&addr, prefix_info->prefix, sizeof(prefix_info->prefix) / 2);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
ifaddr = net_if_ipv6_addr_lookup(&addr, NULL);
|
|
|
|
if (ifaddr && ifaddr->addr_type == NET_ADDR_AUTOCONF) {
|
|
|
|
if (prefix_info->valid_lifetime ==
|
|
|
|
NET_IPV6_ND_INFINITE_LIFETIME) {
|
|
|
|
net_if_addr_set_lf(ifaddr, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RFC 4862 ch 5.5.3 */
|
|
|
|
if ((prefix_info->valid_lifetime > TWO_HOURS) ||
|
|
|
|
(prefix_info->valid_lifetime >
|
|
|
|
remaining_lifetime(ifaddr))) {
|
|
|
|
NET_DBG("Timer updating for address %s "
|
|
|
|
"long lifetime %u secs",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&addr),
|
2018-08-15 12:58:57 +02:00
|
|
|
prefix_info->valid_lifetime);
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_if_ipv6_addr_update_lifetime(
|
|
|
|
ifaddr, prefix_info->valid_lifetime);
|
2018-08-15 12:58:57 +02:00
|
|
|
} else {
|
|
|
|
NET_DBG("Timer updating for address %s "
|
|
|
|
"lifetime %u secs",
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&addr),
|
2018-10-02 13:57:55 +02:00
|
|
|
TWO_HOURS);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_if_ipv6_addr_update_lifetime(ifaddr, TWO_HOURS);
|
|
|
|
}
|
|
|
|
|
|
|
|
net_if_addr_set_lf(ifaddr, false);
|
|
|
|
} else {
|
|
|
|
if (prefix_info->valid_lifetime ==
|
|
|
|
NET_IPV6_ND_INFINITE_LIFETIME) {
|
|
|
|
net_if_ipv6_addr_add(net_pkt_iface(pkt),
|
|
|
|
&addr, NET_ADDR_AUTOCONF, 0);
|
|
|
|
} else {
|
|
|
|
net_if_ipv6_addr_add(net_pkt_iface(pkt),
|
|
|
|
&addr, NET_ADDR_AUTOCONF,
|
|
|
|
prefix_info->valid_lifetime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
static inline bool handle_ra_prefix(struct net_pkt *pkt)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(rapfx_access,
|
|
|
|
struct net_icmpv6_nd_opt_prefix_info);
|
|
|
|
struct net_icmpv6_nd_opt_prefix_info *pfx_info;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t valid_lifetime, preferred_lifetime;
|
2018-12-20 15:26:22 +01:00
|
|
|
|
|
|
|
pfx_info = (struct net_icmpv6_nd_opt_prefix_info *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &rapfx_access);
|
2018-12-20 15:26:22 +01:00
|
|
|
if (!pfx_info) {
|
|
|
|
return false;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &rapfx_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-06-25 09:14:53 +02:00
|
|
|
valid_lifetime = ntohl(pfx_info->valid_lifetime);
|
|
|
|
preferred_lifetime = ntohl(pfx_info->preferred_lifetime);
|
2018-12-20 15:26:22 +01:00
|
|
|
|
2019-06-25 09:14:53 +02:00
|
|
|
if (valid_lifetime >= preferred_lifetime &&
|
2021-10-06 10:06:26 +02:00
|
|
|
!net_ipv6_is_ll_addr((struct in6_addr *)pfx_info->prefix)) {
|
2018-12-20 15:26:22 +01:00
|
|
|
if (pfx_info->flags & NET_ICMPV6_RA_FLAG_ONLINK) {
|
|
|
|
handle_prefix_onlink(pkt, pfx_info);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
if ((pfx_info->flags & NET_ICMPV6_RA_FLAG_AUTONOMOUS) &&
|
2019-06-25 09:14:53 +02:00
|
|
|
valid_lifetime &&
|
2018-12-20 15:26:22 +01:00
|
|
|
(pfx_info->prefix_len == NET_IPV6_DEFAULT_PREFIX_LEN)) {
|
|
|
|
handle_prefix_autonomous(pkt, pfx_info);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
return true;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_6LO_CONTEXT)
|
|
|
|
/* 6lowpan Context Option RFC 6775, 4.2 */
|
2020-05-27 18:26:57 +02:00
|
|
|
static inline bool handle_ra_6co(struct net_pkt *pkt, uint8_t len)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(ctx_access, struct net_icmpv6_nd_opt_6co);
|
|
|
|
struct net_icmpv6_nd_opt_6co *context;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
context = (struct net_icmpv6_nd_opt_6co *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &ctx_access);
|
2018-12-20 15:26:22 +01:00
|
|
|
if (!context) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* RFC 6775, 4.2
|
|
|
|
* Context Length: 8-bit unsigned integer. The number of leading
|
|
|
|
* bits in the Context Prefix field that are valid. The value ranges
|
|
|
|
* from 0 to 128. If it is more than 64, then the Length MUST be 3.
|
|
|
|
*/
|
2019-03-27 02:57:45 +01:00
|
|
|
if ((context->context_len > 64 && len != 3U) ||
|
|
|
|
(context->context_len <= 64U && len != 2U)) {
|
2018-12-20 15:26:22 +01:00
|
|
|
return false;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2019-03-27 02:57:45 +01:00
|
|
|
context->context_len = context->context_len / 8U;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* context_len: The number of leading bits in the Context Prefix
|
2018-12-20 15:26:22 +01:00
|
|
|
* field that are valid. Rest must be set to 0 by the sender and
|
|
|
|
* ignored by the receiver. But since there is no way to make sure
|
|
|
|
* the sender followed the rule, let's make sure rest is set to 0.
|
2018-08-15 12:58:57 +02:00
|
|
|
*/
|
2021-10-06 10:06:26 +02:00
|
|
|
if (context->context_len != sizeof(context->prefix)) {
|
|
|
|
(void)memset(context->prefix + context->context_len, 0,
|
|
|
|
sizeof(context->prefix) - context->context_len);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_6lo_set_context(net_pkt_iface(pkt), context);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
return true;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-12-01 13:20:04 +01:00
|
|
|
static inline bool handle_ra_route_info(struct net_pkt *pkt, uint8_t len)
|
|
|
|
{
|
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(routeinfo_access,
|
|
|
|
struct net_icmpv6_nd_opt_route_info);
|
|
|
|
struct net_icmpv6_nd_opt_route_info *route_info;
|
|
|
|
struct net_route_entry *route;
|
|
|
|
struct in6_addr prefix_buf = { 0 };
|
|
|
|
uint8_t prefix_field_len = (len - 1) * 8;
|
|
|
|
uint32_t route_lifetime;
|
|
|
|
uint8_t prefix_len;
|
|
|
|
uint8_t preference;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
route_info = (struct net_icmpv6_nd_opt_route_info *)
|
|
|
|
net_pkt_get_data(pkt, &routeinfo_access);
|
|
|
|
if (!route_info) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = net_pkt_acknowledge_data(pkt, &routeinfo_access);
|
|
|
|
if (ret < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
prefix_len = route_info->prefix_len;
|
|
|
|
route_lifetime = ntohl(route_info->route_lifetime);
|
|
|
|
preference = route_info->flags.prf;
|
|
|
|
|
|
|
|
ret = net_pkt_read(pkt, &prefix_buf, prefix_field_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_ERR("Error reading prefix, %d", ret);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (route_lifetime == 0) {
|
|
|
|
route = net_route_lookup(net_pkt_orig_iface(pkt), &prefix_buf);
|
|
|
|
if (route != NULL) {
|
|
|
|
ret = net_route_del(route);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Failed to delete route");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
route = net_route_add(net_pkt_orig_iface(pkt),
|
|
|
|
&prefix_buf,
|
|
|
|
prefix_len,
|
2021-12-03 12:00:00 +01:00
|
|
|
(struct in6_addr *)NET_IPV6_HDR(pkt)->src,
|
2021-12-03 17:24:10 +01:00
|
|
|
route_lifetime,
|
|
|
|
preference);
|
2021-12-01 13:20:04 +01:00
|
|
|
if (route == NULL) {
|
|
|
|
NET_DBG("Failed to add route");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-02-20 08:26:15 +01:00
|
|
|
#if defined(CONFIG_NET_IPV6_RA_RDNSS)
|
|
|
|
static inline bool handle_ra_rdnss(struct net_pkt *pkt, uint8_t len)
|
|
|
|
{
|
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(rdnss_access, struct net_icmpv6_nd_opt_rdnss);
|
|
|
|
struct net_icmpv6_nd_opt_rdnss *rdnss;
|
|
|
|
struct dns_resolve_context *ctx;
|
2023-02-21 21:35:24 +01:00
|
|
|
struct sockaddr_in6 dns = {
|
|
|
|
.sin6_family = AF_INET6
|
|
|
|
};
|
2023-02-20 08:26:15 +01:00
|
|
|
const struct sockaddr *dns_servers[] = {
|
|
|
|
(struct sockaddr *)&dns, NULL
|
|
|
|
};
|
|
|
|
size_t rdnss_size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
rdnss = (struct net_icmpv6_nd_opt_rdnss *) net_pkt_get_data(pkt, &rdnss_access);
|
|
|
|
if (!rdnss) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = net_pkt_acknowledge_data(pkt, &rdnss_access);
|
|
|
|
if (ret < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
rdnss_size = len * 8U - 2 - sizeof(struct net_icmpv6_nd_opt_rdnss);
|
|
|
|
if ((rdnss_size % NET_IPV6_ADDR_SIZE) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recursive DNS servers option may present 1 or more addresses,
|
|
|
|
* each 16 bytes in length. DNS servers should be listed in order
|
|
|
|
* of preference, choose the first and skip the rest.
|
|
|
|
*/
|
|
|
|
ret = net_pkt_read(pkt, dns.sin6_addr.s6_addr, NET_IPV6_ADDR_SIZE);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_ERR("Failed to read RDNSS address, %d", ret);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the rest of the DNS servers. */
|
|
|
|
if (net_pkt_skip(pkt, rdnss_size - NET_IPV6_ADDR_SIZE)) {
|
|
|
|
NET_ERR("Failed to skip RDNSS address, %d", ret);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: Handle lifetime. */
|
|
|
|
ctx = dns_resolve_get_default();
|
|
|
|
ret = dns_resolve_reconfigure(ctx, NULL, dns_servers);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_DBG("Failed to set RDNSS resolve address: %d", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
static int handle_ra_input(struct net_icmp_ctx *ctx,
|
|
|
|
struct net_pkt *pkt,
|
|
|
|
struct net_icmp_ip_hdr *hdr,
|
|
|
|
struct net_icmp_hdr *icmp_hdr,
|
|
|
|
void *user_data)
|
2018-08-15 12:58:57 +02:00
|
|
|
{
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ra_access,
|
|
|
|
struct net_icmpv6_ra_hdr);
|
|
|
|
NET_PKT_DATA_ACCESS_DEFINE(nd_access, struct net_icmpv6_nd_opt_hdr);
|
2023-09-21 16:24:35 +02:00
|
|
|
struct net_ipv6_hdr *ip_hdr = hdr->ipv6;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint16_t length = net_pkt_get_len(pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_nbr *nbr = NULL;
|
2018-12-20 15:26:22 +01:00
|
|
|
struct net_icmpv6_nd_opt_hdr *nd_opt_hdr;
|
|
|
|
struct net_icmpv6_ra_hdr *ra_hdr;
|
2018-08-15 12:58:57 +02:00
|
|
|
struct net_if_router *router;
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t mtu, reachable_time, retrans_timer;
|
|
|
|
uint16_t router_lifetime;
|
2018-12-20 15:26:22 +01:00
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
ARG_UNUSED(user_data);
|
|
|
|
|
2023-02-26 15:01:47 +01:00
|
|
|
if (net_if_flag_is_set(net_pkt_iface(pkt), NET_IF_IPV6_NO_ND)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2019-02-20 09:28:18 +01:00
|
|
|
ra_hdr = (struct net_icmpv6_ra_hdr *)net_pkt_get_data(pkt, &ra_access);
|
2018-12-20 15:26:22 +01:00
|
|
|
if (!ra_hdr) {
|
|
|
|
NET_ERR("DROP: NULL RA header");
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2020-02-14 20:19:07 +01:00
|
|
|
dbg_addr_recv("Router Advertisement", &ip_hdr->src, &ip_hdr->dst, pkt);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
net_stats_update_ipv6_nd_recv(net_pkt_iface(pkt));
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
if (((length < (sizeof(struct net_ipv6_hdr) +
|
|
|
|
sizeof(struct net_icmp_hdr) +
|
|
|
|
sizeof(struct net_icmpv6_ra_hdr) +
|
|
|
|
sizeof(struct net_icmpv6_nd_opt_hdr))) ||
|
|
|
|
(ip_hdr->hop_limit != NET_IPV6_ND_HOP_LIMIT) ||
|
2021-10-05 12:23:57 +02:00
|
|
|
!net_ipv6_is_ll_addr((struct in6_addr *)ip_hdr->src)) &&
|
2019-03-27 02:57:45 +01:00
|
|
|
icmp_hdr->code != 0U) {
|
2018-12-20 15:26:22 +01:00
|
|
|
goto drop;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_pkt_acknowledge_data(pkt, &ra_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2019-06-25 09:14:53 +02:00
|
|
|
router_lifetime = ntohs(ra_hdr->router_lifetime);
|
|
|
|
reachable_time = ntohl(ra_hdr->reachable_time);
|
|
|
|
retrans_timer = ntohl(ra_hdr->retrans_timer);
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
if (ra_hdr->cur_hop_limit) {
|
2023-11-28 16:03:10 +01:00
|
|
|
net_if_ipv6_set_hop_limit(net_pkt_iface(pkt),
|
|
|
|
ra_hdr->cur_hop_limit);
|
2018-08-15 12:58:57 +02:00
|
|
|
NET_DBG("New hop limit %d",
|
|
|
|
net_if_ipv6_get_hop_limit(net_pkt_iface(pkt)));
|
|
|
|
}
|
|
|
|
|
2019-06-25 09:14:53 +02:00
|
|
|
if (reachable_time && reachable_time <= MAX_REACHABLE_TIME &&
|
2018-08-15 12:58:57 +02:00
|
|
|
(net_if_ipv6_get_reachable_time(net_pkt_iface(pkt)) !=
|
2019-06-25 09:14:53 +02:00
|
|
|
reachable_time)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
net_if_ipv6_set_base_reachable_time(net_pkt_iface(pkt),
|
2019-06-25 09:14:53 +02:00
|
|
|
reachable_time);
|
2018-08-15 12:58:57 +02:00
|
|
|
net_if_ipv6_set_reachable_time(
|
|
|
|
net_pkt_iface(pkt)->config.ip.ipv6);
|
|
|
|
}
|
|
|
|
|
2019-06-25 09:14:53 +02:00
|
|
|
if (retrans_timer) {
|
2018-08-15 12:58:57 +02:00
|
|
|
net_if_ipv6_set_retrans_timer(net_pkt_iface(pkt),
|
2018-12-20 15:26:22 +01:00
|
|
|
ra_hdr->retrans_timer);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_pkt_set_ipv6_ext_opt_len(pkt, sizeof(struct net_icmpv6_ra_hdr));
|
|
|
|
length -= (sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2022-09-28 12:18:26 +02:00
|
|
|
|
|
|
|
/* Add neighbor cache entry using link local address, regardless of link layer address
|
|
|
|
* presence in Router Advertisement.
|
|
|
|
*/
|
|
|
|
nbr = net_ipv6_nbr_add(net_pkt_iface(pkt), (struct in6_addr *)NET_IPV6_HDR(pkt)->src, NULL,
|
|
|
|
true, NET_IPV6_NBR_STATE_INCOMPLETE);
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
while (nd_opt_hdr) {
|
|
|
|
net_pkt_acknowledge_data(pkt, &nd_access);
|
|
|
|
|
|
|
|
switch (nd_opt_hdr->type) {
|
2018-08-15 12:58:57 +02:00
|
|
|
case NET_ICMPV6_ND_OPT_SLLAO:
|
2022-09-28 12:18:26 +02:00
|
|
|
/* Update existing neighbor cache entry with link layer address. */
|
2018-12-20 15:26:22 +01:00
|
|
|
nbr = handle_ra_neighbor(pkt, nd_opt_hdr->len);
|
|
|
|
if (!nbr) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case NET_ICMPV6_ND_OPT_MTU:
|
|
|
|
/* MTU has reserved 2 bytes, so skip it. */
|
2018-12-20 15:26:22 +01:00
|
|
|
if (net_pkt_skip(pkt, 2) ||
|
2019-02-20 09:40:48 +01:00
|
|
|
net_pkt_read_be32(pkt, &mtu)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mtu < MIN_IPV6_MTU || mtu > MAX_IPV6_MTU) {
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_ERR("DROP: Unsupported MTU %u, min is %u, "
|
2018-08-15 12:58:57 +02:00
|
|
|
"max is %u",
|
|
|
|
mtu, MIN_IPV6_MTU, MAX_IPV6_MTU);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_if_set_mtu(net_pkt_iface(pkt), mtu);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case NET_ICMPV6_ND_OPT_PREFIX_INFO:
|
2020-05-28 12:01:59 +02:00
|
|
|
if (nd_opt_hdr->len != 4) {
|
|
|
|
NET_ERR("DROP: Invalid %s length (%d)",
|
|
|
|
"prefix opt", nd_opt_hdr->len);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
if (!handle_ra_prefix(pkt)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
#if defined(CONFIG_NET_6LO_CONTEXT)
|
|
|
|
case NET_ICMPV6_ND_OPT_6CO:
|
|
|
|
/* RFC 6775, 4.2 (Length)*/
|
2019-03-27 02:57:45 +01:00
|
|
|
if (!(nd_opt_hdr->len == 2U || nd_opt_hdr->len == 3U)) {
|
2020-05-28 12:01:59 +02:00
|
|
|
NET_ERR("DROP: Invalid %s length %d",
|
|
|
|
"6CO", nd_opt_hdr->len);
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
if (!handle_ra_6co(pkt, nd_opt_hdr->len)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case NET_ICMPV6_ND_OPT_ROUTE:
|
2021-12-01 13:20:04 +01:00
|
|
|
if (!IS_ENABLED(CONFIG_NET_ROUTE)) {
|
|
|
|
NET_DBG("Route option skipped");
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RFC 4191, ch. 2.3 */
|
|
|
|
if (nd_opt_hdr->len == 0U || nd_opt_hdr->len > 3U) {
|
|
|
|
NET_ERR("DROP: Invalid %s length (%d)",
|
|
|
|
"route info opt", nd_opt_hdr->len);
|
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2021-12-01 13:20:04 +01:00
|
|
|
if (!handle_ra_route_info(pkt, nd_opt_hdr->len)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2018-08-15 12:58:57 +02:00
|
|
|
#if defined(CONFIG_NET_IPV6_RA_RDNSS)
|
|
|
|
case NET_ICMPV6_ND_OPT_RDNSS:
|
2023-02-20 08:26:15 +01:00
|
|
|
if (!handle_ra_rdnss(pkt, nd_opt_hdr->len)) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
break;
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case NET_ICMPV6_ND_OPT_DNSSL:
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_DBG("DNSSL option skipped");
|
2018-08-15 12:58:57 +02:00
|
|
|
goto skip;
|
|
|
|
|
|
|
|
default:
|
2018-12-20 15:26:22 +01:00
|
|
|
NET_DBG("Unknown ND option 0x%x", nd_opt_hdr->type);
|
2018-08-15 12:58:57 +02:00
|
|
|
skip:
|
2019-03-27 02:57:45 +01:00
|
|
|
if (net_pkt_skip(pkt, nd_opt_hdr->len * 8U - 2)) {
|
2018-08-15 12:58:57 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2018-12-20 15:26:22 +01:00
|
|
|
|
|
|
|
nd_opt_hdr = (struct net_icmpv6_nd_opt_hdr *)
|
2019-02-20 09:28:18 +01:00
|
|
|
net_pkt_get_data(pkt, &nd_access);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2021-10-05 12:23:57 +02:00
|
|
|
router = net_if_ipv6_router_lookup(net_pkt_iface(pkt),
|
|
|
|
(struct in6_addr *)ip_hdr->src);
|
2018-08-15 12:58:57 +02:00
|
|
|
if (router) {
|
2019-06-25 09:14:53 +02:00
|
|
|
if (!router_lifetime) {
|
2018-08-15 12:58:57 +02:00
|
|
|
/* TODO: Start rs_timer on iface if no routers
|
|
|
|
* at all available on iface.
|
|
|
|
*/
|
|
|
|
net_if_ipv6_router_rm(router);
|
|
|
|
} else {
|
|
|
|
if (nbr) {
|
|
|
|
net_ipv6_nbr_data(nbr)->is_router = true;
|
|
|
|
}
|
|
|
|
|
2018-12-20 15:26:22 +01:00
|
|
|
net_if_ipv6_router_update_lifetime(
|
2019-06-25 09:14:53 +02:00
|
|
|
router, router_lifetime);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
net_if_ipv6_router_add(net_pkt_iface(pkt),
|
2021-10-05 12:23:57 +02:00
|
|
|
(struct in6_addr *)ip_hdr->src,
|
|
|
|
router_lifetime);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_lock();
|
2018-08-15 12:58:57 +02:00
|
|
|
if (nbr && net_ipv6_nbr_data(nbr)->pending) {
|
|
|
|
NET_DBG("Sending pending pkt %p to %s",
|
|
|
|
net_ipv6_nbr_data(nbr)->pending,
|
2022-06-20 07:43:37 +02:00
|
|
|
net_sprint_ipv6_addr(&NET_IPV6_HDR(net_ipv6_nbr_data(nbr)->pending)->dst));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
if (net_send_data(net_ipv6_nbr_data(nbr)->pending) < 0) {
|
|
|
|
net_pkt_unref(net_ipv6_nbr_data(nbr)->pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
nbr_clear_ns_pending(net_ipv6_nbr_data(nbr));
|
|
|
|
}
|
2024-02-08 16:24:12 +01:00
|
|
|
net_ipv6_nbr_unlock();
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
/* Cancel the RS timer on iface */
|
2019-05-21 12:07:08 +02:00
|
|
|
net_if_stop_rs(net_pkt_iface(pkt));
|
2018-08-15 12:58:57 +02:00
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return 0;
|
2018-08-15 12:58:57 +02:00
|
|
|
|
|
|
|
drop:
|
|
|
|
net_stats_update_ipv6_nd_drop(net_pkt_iface(pkt));
|
|
|
|
|
2023-09-21 16:24:35 +02:00
|
|
|
return -EIO;
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6_ND */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
|
2023-09-21 16:24:35 +02:00
|
|
|
static struct net_icmp_ctx ns_ctx;
|
|
|
|
static struct net_icmp_ctx na_ctx;
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif /* CONFIG_NET_IPV6_NBR_CACHE */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
2023-09-21 16:24:35 +02:00
|
|
|
static struct net_icmp_ctx ra_ctx;
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif /* CONFIG_NET_IPV6_ND */
|
|
|
|
|
|
|
|
void net_ipv6_nbr_init(void)
|
|
|
|
{
|
2023-09-21 16:24:35 +02:00
|
|
|
int ret;
|
|
|
|
|
2018-08-15 12:58:57 +02:00
|
|
|
#if defined(CONFIG_NET_IPV6_NBR_CACHE)
|
2023-09-21 16:24:35 +02:00
|
|
|
ret = net_icmp_init_ctx(&ns_ctx, NET_ICMPV6_NS, 0, handle_ns_input);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV6_NS),
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = net_icmp_init_ctx(&na_ctx, NET_ICMPV6_NA, 0, handle_na_input);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV6_NA),
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
2021-03-31 17:31:30 +02:00
|
|
|
k_work_init_delayable(&ipv6_ns_reply_timer, ipv6_ns_reply_timeout);
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV6_ND)
|
2023-09-21 16:24:35 +02:00
|
|
|
ret = net_icmp_init_ctx(&ra_ctx, NET_ICMPV6_RA, 0, handle_ra_input);
|
|
|
|
if (ret < 0) {
|
|
|
|
NET_ERR("Cannot register %s handler (%d)", STRINGIFY(NET_ICMPV6_RA),
|
|
|
|
ret);
|
|
|
|
}
|
|
|
|
|
2021-03-31 17:31:30 +02:00
|
|
|
k_work_init_delayable(&ipv6_nd_reachable_timer,
|
|
|
|
ipv6_nd_reachable_timeout);
|
2018-08-15 12:58:57 +02:00
|
|
|
#endif
|
2023-09-21 16:24:35 +02:00
|
|
|
|
|
|
|
ARG_UNUSED(ret);
|
2018-08-15 12:58:57 +02:00
|
|
|
}
|