blob: c89c643967538e5c64aca615c399c8b1e370f0dc [file] [log] [blame]
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_DECLARE(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
#include <net/net_core.h>
#include <net/net_pkt.h>
#include <net/openthread.h>
#include <openthread/ip6.h>
#include <openthread/thread.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 u8_t *address)
{
const otMeshLocalPrefix *ml_prefix =
otThreadGetMeshLocalPrefix(context->instance);
return (memcmp(address, ml_prefix->m8, sizeof(ml_prefix)) == 0);
}
int pkt_list_add(struct openthread_context *context, struct net_pkt *pkt)
{
u16_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;
}
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(context->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",
log_strdup(net_addr_ntop(AF_INET6,
(struct in6_addr *)(&address->mAddress),
buf, sizeof(buf))));
}
if_addr = net_if_ipv6_addr_add(
context->iface,
(struct in6_addr *)(&address->mAddress),
NET_ADDR_AUTOCONF, 0);
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);
}
}
void add_ipv6_addr_to_ot(struct openthread_context *context)
{
struct net_if *iface = context->iface;
struct otNetifAddress addr;
struct net_if_ipv6 *ipv6;
int i;
(void)memset(&addr, 0, sizeof(addr));
if (net_if_config_ipv6_get(iface, &ipv6) < 0) {
NET_DBG("Cannot allocate IPv6 address");
return;
}
/* save the last added IP address for this interface */
for (i = NET_IF_MAX_IPV6_ADDR - 1; i >= 0; i--) {
if (ipv6->unicast[i].is_used) {
memcpy(&addr.mAddress,
&ipv6->unicast[i].address.in6_addr,
sizeof(addr.mAddress));
break;
}
}
ipv6->unicast[i].is_mesh_local = is_mesh_local(
context, ipv6->unicast[i].address.in6_addr.s6_addr);
addr.mValid = true;
addr.mPreferred = true;
addr.mPrefixLength = 64;
otIp6AddUnicastAddress(context->instance, &addr);
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Added %s",
log_strdup(net_addr_ntop(AF_INET6,
&addr.mAddress, buf,
sizeof(buf))));
}
}
void add_ipv6_maddr_to_ot(struct openthread_context *context)
{
struct otIp6Address addr;
struct net_if_ipv6 *ipv6;
int i;
if (net_if_config_ipv6_get(context->iface, &ipv6) < 0) {
NET_DBG("Cannot allocate IPv6 address");
return;
}
/* save the last added IP address for this interface */
for (i = NET_IF_MAX_IPV6_MADDR - 1; i >= 0; i--) {
if (ipv6->mcast[i].is_used) {
memcpy(&addr,
&ipv6->mcast[i].address.in6_addr,
sizeof(addr));
break;
}
}
otIp6SubscribeMulticastAddress(context->instance, &addr);
if (CONFIG_OPENTHREAD_L2_LOG_LEVEL == LOG_LEVEL_DBG) {
char buf[NET_IPV6_ADDR_LEN];
NET_DBG("Added multicast %s",
log_strdup(net_addr_ntop(AF_INET6, &addr,
buf, sizeof(buf))));
}
}
void add_ipv6_maddr_to_zephyr(struct openthread_context *context)
{
const otNetifMulticastAddress *maddress;
for (maddress = otIp6GetMulticastAddresses(context->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",
log_strdup(net_addr_ntop(AF_INET6,
(struct in6_addr *)
(&maddress->mAddress),
buf, sizeof(buf))));
}
net_if_ipv6_maddr_add(context->iface,
(struct in6_addr *)(&maddress->mAddress));
}
}
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(context->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",
log_strdup(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(context->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",
log_strdup(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);
}
}
}