blob: 58ae1a559e58b928cf2b7376c0cc2a509239c94b [file] [log] [blame]
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "openthread/instance.h"
#include "openthread/ip6.h"
#include "openthread/trel.h"
#include "openthread_border_router.h"
#include <common/code_utils.hpp>
#include <zephyr/net/socket.h>
#include <zephyr/net/socket_service.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_ip.h>
#include "sockets_internal.h"
#define MAX_SERVICES 1
static struct zsock_pollfd sockfd_udp[MAX_SERVICES];
static int trel_sock = -1;
static struct otInstance *ot_instance_ptr;
static otPlatTrelCounters trel_counters;
static bool trel_is_enabled;
static void trel_receive_handler(struct net_socket_service_event *evt);
static void process_trel_message(struct otbr_msg_ctx *msg_ctx_ptr);
NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(trel_udp_service, trel_receive_handler, MAX_SERVICES);
void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
{
struct sockaddr_in6 addr = {.sin6_family = AF_INET6,
.sin6_port = 0,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_scope_id = 0};
socklen_t len = sizeof(addr);
trel_sock = zsock_socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
VerifyOrExit(trel_sock >= 0);
VerifyOrExit(zsock_bind(trel_sock, (struct sockaddr *)&addr, sizeof(addr)) == 0);
VerifyOrExit(zsock_getsockname(trel_sock, (struct sockaddr *)&addr, &len) == 0);
*aUdpPort = ntohs(addr.sin6_port);
otPlatTrelResetCounters(aInstance);
trel_is_enabled = true;
exit:
return;
}
void otPlatTrelDisable(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
VerifyOrExit(trel_is_enabled);
VerifyOrExit(trel_sock != -1);
VerifyOrExit(zsock_close(trel_sock) == 0);
sockfd_udp[0].fd = -1;
trel_sock = -1;
trel_is_enabled = false;
exit:
return;
}
void otPlatTrelSend(otInstance *aInstance, const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen,
const otSockAddr *aDestSockAddr)
{
VerifyOrExit(trel_is_enabled);
struct sockaddr_in6 dest_sock_addr = {.sin6_family = AF_INET6,
.sin6_port = htons(aDestSockAddr->mPort),
.sin6_addr = {{{0}}},
.sin6_scope_id = 0};
memcpy(&dest_sock_addr.sin6_addr, &aDestSockAddr->mAddress, sizeof(otIp6Address));
if (zsock_sendto(trel_sock, aUdpPayload, aUdpPayloadLen, 0,
(struct sockaddr *)&dest_sock_addr,
sizeof(dest_sock_addr)) == aUdpPayloadLen) {
trel_counters.mTxBytes += aUdpPayloadLen;
trel_counters.mTxPackets++;
} else {
trel_counters.mTxFailure++;
}
exit:
return;
}
const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return &trel_counters;
}
void otPlatTrelResetCounters(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
memset(&trel_counters, 0, sizeof(trel_counters));
}
static void trel_receive_handler(struct net_socket_service_event *evt)
{
struct sockaddr_in6 addr = {0};
socklen_t addrlen = sizeof(addr);
ssize_t len = 0;
struct otbr_msg_ctx *req = NULL;
trel_counters.mRxPackets++;
VerifyOrExit(evt->event.revents & ZSOCK_POLLIN);
VerifyOrExit(openthread_border_router_allocate_message((void **)&req) == OT_ERROR_NONE);
len = zsock_recvfrom(trel_sock, req->buffer, sizeof(req->buffer), 0,
(struct sockaddr *)&addr, &addrlen);
VerifyOrExit(len > 0);
trel_counters.mRxBytes += len;
memcpy(&req->sock_addr.mAddress, &addr.sin6_addr, sizeof(otIp6Address));
req->length = (uint16_t)len;
req->sock_addr.mPort = ntohs(addr.sin6_port);
req->cb = process_trel_message;
openthread_border_router_post_message(req);
exit:
return;
}
static void process_trel_message(struct otbr_msg_ctx *msg_ctx_ptr)
{
otPlatTrelHandleReceived(ot_instance_ptr, msg_ctx_ptr->buffer, msg_ctx_ptr->length,
&msg_ctx_ptr->sock_addr);
}
otError trel_plat_init(otInstance *instance, struct net_if *ail_iface_ptr)
{
otError error = OT_ERROR_NONE;
struct ifreq if_req = {0};
char name[CONFIG_NET_INTERFACE_NAME_LEN + 1] = {0};
ot_instance_ptr = instance;
VerifyOrExit(net_if_get_name(ail_iface_ptr, name,
CONFIG_NET_INTERFACE_NAME_LEN) > 0,
error = OT_ERROR_FAILED);
memcpy(if_req.ifr_name, name, MIN(sizeof(name) - 1, sizeof(if_req.ifr_name) - 1));
VerifyOrExit(zsock_setsockopt(trel_sock, SOL_SOCKET, SO_BINDTODEVICE, &if_req,
sizeof(if_req)) == 0,
error = OT_ERROR_FAILED);
sockfd_udp[0].fd = trel_sock;
sockfd_udp[0].events = ZSOCK_POLLIN;
VerifyOrExit(net_socket_service_register(&trel_udp_service, sockfd_udp,
ARRAY_SIZE(sockfd_udp), NULL) == 0,
error = OT_ERROR_FAILED);
exit:
return error;
}