zephyr/subsys/net/l2/openthread/openthread_utils.c
Robert Lubos be46c94e3e net: l2: openthread: Fix error logs on adding already present address
Error checking of otIp6AddUnicastAddress() and
otIp6SubscribeMulticastAddress() was added recently, however it wasn't
taken into account that those APIs return an error on attempt to
register an IPv6 address that is already present on the OT interface.
Therefore, add more specific error checks, to return silently in case
address was already present.

As those two APIs are not very consistent, and otIp6AddUnicastAddress()
returns OT_ERROR_INVALID_ARGS in such cases, add an extra check if the
address is already present before attempting to register the address.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2025-05-13 16:23:37 +02:00

397 lines
9.6 KiB
C

/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/openthread.h>
#include <openthread/ip6.h>
#include <openthread/thread.h>
#include "net_private.h"
#include "openthread_utils.h"
#define ALOC16_MASK 0xfc
static bool is_anycast_locator(const otNetifAddress *address)
{
return address->mAddress.mFields.m16[4] == htons(0x0000) &&
address->mAddress.mFields.m16[5] == htons(0x00ff) &&
address->mAddress.mFields.m16[6] == htons(0xfe00) &&
address->mAddress.mFields.m8[14] == ALOC16_MASK;
}
static bool is_mesh_local(struct openthread_context *context,
const uint8_t *address)
{
const otMeshLocalPrefix *ml_prefix =
otThreadGetMeshLocalPrefix(openthread_get_default_instance());
return (memcmp(address, ml_prefix->m8, sizeof(ml_prefix->m8)) == 0);
}
int pkt_list_add(struct openthread_context *context, struct net_pkt *pkt)
{
uint16_t i_idx = context->pkt_list_in_idx;
if (context->pkt_list_full) {
return -ENOMEM;
}
i_idx++;
if (i_idx == CONFIG_OPENTHREAD_PKT_LIST_SIZE) {
i_idx = 0U;
}
if (i_idx == context->pkt_list_out_idx) {
context->pkt_list_full = 1U;
}
context->pkt_list[context->pkt_list_in_idx].pkt = pkt;
context->pkt_list_in_idx = i_idx;
return 0;
}
void pkt_list_remove_first(struct openthread_context *context)
{
uint16_t idx = context->pkt_list_in_idx;
if (idx == 0U) {
idx = CONFIG_OPENTHREAD_PKT_LIST_SIZE - 1;
} else {
idx--;
}
context->pkt_list_in_idx = idx;
if (context->pkt_list_full) {
context->pkt_list_full = 0U;
}
}
struct net_pkt *pkt_list_peek(struct openthread_context *context)
{
if ((context->pkt_list_in_idx == context->pkt_list_out_idx) &&
(!context->pkt_list_full)) {
return NULL;
}
return context->pkt_list[context->pkt_list_out_idx].pkt;
}
void pkt_list_remove_last(struct openthread_context *context)
{
if ((context->pkt_list_in_idx == context->pkt_list_out_idx) &&
(!context->pkt_list_full)) {
return;
}
context->pkt_list_out_idx++;
if (context->pkt_list_out_idx == CONFIG_OPENTHREAD_PKT_LIST_SIZE) {
context->pkt_list_out_idx = 0U;
}
context->pkt_list_full = 0U;
}
void add_ipv6_addr_to_zephyr(struct openthread_context *context)
{
const otNetifAddress *address;
struct net_if_addr *if_addr;
for (address = otIp6GetUnicastAddresses(openthread_get_default_instance());
address; address = address->mNext) {
if (address->mRloc || is_anycast_locator(address)) {
continue;
}
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Adding %s",
net_addr_ntop(AF_INET6,
(struct in6_addr *)(&address->mAddress),
buf, sizeof(buf)));
}
/* Thread and SLAAC are clearly AUTOCONF, handle
* manual/NCP addresses in the same way
*/
if ((address->mAddressOrigin == OT_ADDRESS_ORIGIN_THREAD) ||
(address->mAddressOrigin == OT_ADDRESS_ORIGIN_SLAAC)) {
if_addr = net_if_ipv6_addr_add(
context->iface,
(struct in6_addr *)(&address->mAddress),
NET_ADDR_AUTOCONF, 0);
} else if (address->mAddressOrigin ==
OT_ADDRESS_ORIGIN_DHCPV6) {
if_addr = net_if_ipv6_addr_add(
context->iface,
(struct in6_addr *)(&address->mAddress),
NET_ADDR_DHCP, 0);
} else if (address->mAddressOrigin ==
OT_ADDRESS_ORIGIN_MANUAL) {
if_addr = net_if_ipv6_addr_add(
context->iface,
(struct in6_addr *)(&address->mAddress),
NET_ADDR_MANUAL, 0);
} else {
NET_ERR("Unknown OpenThread address origin ignored.");
continue;
}
if (if_addr == NULL) {
NET_ERR("Cannot add OpenThread unicast address");
continue;
}
if_addr->is_mesh_local = is_mesh_local(
context, address->mAddress.mFields.m8);
/* Mark address as deprecated if it is not preferred. */
if_addr->addr_state =
address->mPreferred ? NET_ADDR_PREFERRED : NET_ADDR_DEPRECATED;
}
}
void add_ipv6_addr_to_ot(struct openthread_context *context,
const struct in6_addr *addr6)
{
struct otNetifAddress addr = { 0 };
struct net_if_ipv6 *ipv6;
struct net_if_addr *if_addr = NULL;
otError error;
int i;
/* IPv6 struct should've already been allocated when we get an
* address added event.
*/
ipv6 = context->iface->config.ip.ipv6;
if (ipv6 == NULL) {
NET_ERR("No IPv6 container allocated");
return;
}
/* Find the net_if_addr structure containing the newly added address. */
for (i = NET_IF_MAX_IPV6_ADDR - 1; i >= 0; i--) {
if (ipv6->unicast[i].is_used &&
net_ipv6_addr_cmp(&ipv6->unicast[i].address.in6_addr,
addr6)) {
if_addr = &ipv6->unicast[i];
break;
}
}
if (if_addr == NULL) {
NET_ERR("No corresponding net_if_addr found");
return;
}
memcpy(&addr.mAddress, addr6, sizeof(addr.mAddress));
if_addr->is_mesh_local = is_mesh_local(
context, ipv6->unicast[i].address.in6_addr.s6_addr);
addr.mValid = true;
addr.mPreferred = (if_addr->addr_state == NET_ADDR_PREFERRED);
addr.mPrefixLength = 64;
if (if_addr->addr_type == NET_ADDR_AUTOCONF) {
addr.mAddressOrigin = OT_ADDRESS_ORIGIN_SLAAC;
} else if (if_addr->addr_type == NET_ADDR_DHCP) {
addr.mAddressOrigin = OT_ADDRESS_ORIGIN_DHCPV6;
} else if (if_addr->addr_type == NET_ADDR_MANUAL) {
addr.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
} else {
NET_ERR("Unknown address type");
return;
}
openthread_mutex_lock();
if (!otIp6HasUnicastAddress(openthread_get_default_instance(),
&addr.mAddress)) {
error = otIp6AddUnicastAddress(openthread_get_default_instance(),
&addr);
} else {
error = OT_ERROR_ALREADY;
}
openthread_mutex_unlock();
if (error == OT_ERROR_ALREADY) {
return;
}
if (error != OT_ERROR_NONE) {
NET_ERR("Failed to add IPv6 unicast address %s [%d]",
net_sprint_ipv6_addr(addr6), error);
} else {
NET_DBG("Added %s", net_sprint_ipv6_addr(addr6));
}
}
void add_ipv6_maddr_to_ot(struct openthread_context *context,
const struct in6_addr *addr6)
{
struct otIp6Address addr;
otError error;
memcpy(&addr, addr6, sizeof(addr));
openthread_mutex_lock();
error = otIp6SubscribeMulticastAddress(openthread_get_default_instance(), &addr);
openthread_mutex_unlock();
if (error == OT_ERROR_ALREADY) {
return;
}
if (error != OT_ERROR_NONE) {
NET_ERR("Failed to add IPv6 multicast address %s [%d]",
net_sprint_ipv6_addr(addr6), error);
} else {
NET_DBG("Added %s", net_sprint_ipv6_addr(addr6));
}
}
void add_ipv6_maddr_to_zephyr(struct openthread_context *context)
{
const otNetifMulticastAddress *maddress;
struct net_if_mcast_addr *zmaddr;
for (maddress = otIp6GetMulticastAddresses(openthread_get_default_instance());
maddress; maddress = maddress->mNext) {
if (net_if_ipv6_maddr_lookup(
(struct in6_addr *)(&maddress->mAddress),
&context->iface) != NULL) {
continue;
}
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Adding multicast %s",
net_addr_ntop(AF_INET6,
(struct in6_addr *)
(&maddress->mAddress),
buf, sizeof(buf)));
}
zmaddr = net_if_ipv6_maddr_add(context->iface,
(struct in6_addr *)(&maddress->mAddress));
if (zmaddr &&
!(net_if_ipv6_maddr_is_joined(zmaddr) ||
net_ipv6_is_addr_mcast_iface(
(struct in6_addr *)(&maddress->mAddress)) ||
net_ipv6_is_addr_mcast_link_all_nodes(
(struct in6_addr *)(&maddress->mAddress)))) {
net_if_ipv6_maddr_join(context->iface, zmaddr);
}
}
}
void rm_ipv6_addr_from_zephyr(struct openthread_context *context)
{
struct in6_addr *ot_addr;
struct net_if_addr *zephyr_addr;
struct net_if_ipv6 *ipv6;
int i;
if (net_if_config_ipv6_get(context->iface, &ipv6) < 0) {
NET_DBG("Cannot find IPv6 address");
return;
}
for (i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) {
const otNetifAddress *address;
bool used = false;
zephyr_addr = &ipv6->unicast[i];
if (!zephyr_addr->is_used) {
continue;
}
for (address = otIp6GetUnicastAddresses(openthread_get_default_instance());
address; address = address->mNext) {
ot_addr = (struct in6_addr *)(&address->mAddress);
if (net_ipv6_addr_cmp(ot_addr,
&zephyr_addr->address.in6_addr)) {
used = true;
break;
}
}
if (!used) {
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Removing %s",
net_addr_ntop(AF_INET6,
&zephyr_addr->address.in6_addr,
buf, sizeof(buf)));
}
net_if_ipv6_addr_rm(context->iface,
&zephyr_addr->address.in6_addr);
}
}
}
void rm_ipv6_maddr_from_zephyr(struct openthread_context *context)
{
struct in6_addr *ot_addr;
struct net_if_mcast_addr *zephyr_addr;
struct net_if_ipv6 *ipv6;
int i;
if (net_if_config_ipv6_get(context->iface, &ipv6) < 0) {
NET_DBG("Cannot find IPv6 address");
return;
}
for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
const otNetifMulticastAddress *maddress;
bool used = false;
zephyr_addr = &ipv6->mcast[i];
if (!zephyr_addr->is_used) {
continue;
}
for (maddress = otIp6GetMulticastAddresses(openthread_get_default_instance());
maddress; maddress = maddress->mNext) {
ot_addr = (struct in6_addr *)(&maddress->mAddress);
if (net_ipv6_addr_cmp(ot_addr,
&zephyr_addr->address.in6_addr)) {
used = true;
break;
}
}
if (!used) {
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Removing multicast %s",
net_addr_ntop(AF_INET6,
&zephyr_addr->address.in6_addr,
buf, sizeof(buf)));
}
net_if_ipv6_maddr_rm(context->iface,
&zephyr_addr->address.in6_addr);
}
}
}