blob: e3ba036f941360bdcfff59c8d184204aa4d92381 [file] [log] [blame]
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "openthread/udp.h"
#include "openthread/ip6.h"
#include "openthread/platform/udp.h"
#include "openthread_border_router.h"
#include "sockets_internal.h"
#include <common/code_utils.hpp>
#include <errno.h>
#include <openthread.h>
#include <openthread/logging.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/kernel.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/socket_service.h>
#include <zephyr/posix/arpa/inet.h>
#include <zephyr/posix/unistd.h>
#include <zephyr/sys/util.h>
static struct zsock_pollfd sockfd_udp[CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES];
static struct otInstance *ot_instance_ptr;
static struct net_if *ot_iface_ptr, *ail_iface_ptr;
static uint32_t ot_iface_index, ail_iface_index;
static uint8_t sock_cnt;
static void udp_receive_handler(struct net_socket_service_event *evt);
NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(handle_udp_receive, udp_receive_handler,
CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES);
otError udp_plat_init(otInstance *ot_instance, struct net_if *ail_iface, struct net_if *ot_iface)
{
ot_instance_ptr = ot_instance;
ot_iface_ptr = ot_iface;
ail_iface_ptr = ail_iface;
ot_iface_index = (uint32_t)net_if_get_by_iface(ot_iface);
ail_iface_index = (uint32_t)net_if_get_by_iface(ail_iface);
for (uint8_t i = 0; i < CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES; i++) {
sockfd_udp[i].fd = -1;
}
return OT_ERROR_NONE;
}
void udp_plat_deinit(void)
{
ARRAY_FOR_EACH(sockfd_udp, idx) {
if (sockfd_udp[idx].fd != -1) {
zsock_close(sockfd_udp[idx].fd);
sockfd_udp[idx].fd = -1;
}
}
}
otError otPlatUdpSocket(otUdpSocket *aUdpSocket)
{
otError error = OT_ERROR_NONE;
int sock = -1;
uint8_t idx;
VerifyOrExit(aUdpSocket != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(sock_cnt < CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES,
error = OT_ERROR_INVALID_STATE);
sock = zsock_socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
VerifyOrExit(sock >= 0, error = OT_ERROR_FAILED);
#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV4_MAPPING_TO_IPV6)
int off = 0;
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) == 0,
error = OT_ERROR_FAILED);
#endif
aUdpSocket->mHandle = INT_TO_POINTER(sock);
for (idx = 0; idx < CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES; idx++) {
if (sockfd_udp[idx].fd < 0) {
sockfd_udp[idx].fd = sock;
sockfd_udp[idx].events = ZSOCK_POLLIN;
sock_cnt++;
break;
}
}
VerifyOrExit(net_socket_service_register(&handle_udp_receive, sockfd_udp,
ARRAY_SIZE(sockfd_udp), NULL) == 0,
error = OT_ERROR_FAILED);
exit:
return error;
}
otError otPlatUdpClose(otUdpSocket *aUdpSocket)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
int sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
VerifyOrExit(zsock_close(sock) == 0, error = OT_ERROR_FAILED);
aUdpSocket->mHandle = NULL;
sock_cnt--;
for (uint8_t i = 0; i < CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER_MAX_UDP_SERVICES; i++) {
if (sockfd_udp[i].fd == sock) {
sockfd_udp[i].fd = -1;
break;
}
}
exit:
return error;
}
otError otPlatUdpBind(otUdpSocket *aUdpSocket)
{
otError error = OT_ERROR_NONE;
int sock = -1;
struct sockaddr_in6 addr = {0};
int on = 1;
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
VerifyOrExit(sock >= 0, error = OT_ERROR_INVALID_ARGS);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(aUdpSocket->mSockName.mPort);
memcpy(&addr.sin6_addr, &aUdpSocket->mSockName.mAddress, sizeof(otIp6Address));
VerifyOrExit(zsock_bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0,
error = OT_ERROR_FAILED);
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) == 0,
error = OT_ERROR_FAILED);
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) == 0,
error = OT_ERROR_FAILED);
exit:
return error;
}
otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier)
{
otError error = OT_ERROR_NONE;
int sock = -1;
char name[CONFIG_NET_INTERFACE_NAME_LEN + 1] = {0};
struct ifreq if_req = {0};
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
VerifyOrExit(sock >= 0, error = OT_ERROR_INVALID_ARGS);
switch (aNetifIdentifier) {
case OT_NETIF_UNSPECIFIED:
VerifyOrExit(zsock_setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) == 0,
error = OT_ERROR_FAILED);
break;
case OT_NETIF_THREAD_HOST:
VerifyOrExit(net_if_get_name(ot_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(sock, SOL_SOCKET, SO_BINDTODEVICE, &if_req,
sizeof(if_req)) == 0,
error = OT_ERROR_FAILED);
break;
case OT_NETIF_THREAD_INTERNAL:
assert(false);
case OT_NETIF_BACKBONE:
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(sock, SOL_SOCKET, SO_BINDTODEVICE, &if_req,
sizeof(if_req)) == 0,
error = OT_ERROR_FAILED);
break;
default:
break;
}
exit:
return error;
}
otError otPlatUdpConnect(otUdpSocket *aUdpSocket)
{
otError error = OT_ERROR_NONE;
int sock = -1;
struct sockaddr_in6 addr = {0};
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
if (aUdpSocket->mPeerName.mPort != 0 &&
!otIp6IsAddressUnspecified(&aUdpSocket->mPeerName.mAddress)) {
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, &aUdpSocket->mPeerName.mAddress, sizeof(otIp6Address));
addr.sin6_port = htons(aUdpSocket->mPeerName.mPort);
VerifyOrExit(zsock_connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0,
error = OT_ERROR_FAILED);
}
exit:
return error;
}
otError otPlatUdpSend(otUdpSocket *aUdpSocket,
otMessage *aMessage,
const otMessageInfo *aMessageInfo)
{
otError error = OT_ERROR_NONE;
struct otbr_msg_ctx *req = NULL;
uint8_t control_buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))] = {0};
uint16_t len = otMessageGetLength(aMessage);
size_t control_len = 0;
struct sockaddr_in6 peer = {0};
int sock = -1;
struct iovec iov = {0};
struct msghdr msg_hdr = {0};
struct cmsghdr *cmsg_hdr = NULL;
int hop_limit = 0;
int on = 1, off = 0;
int optval;
socklen_t optlen = sizeof(optval);
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
VerifyOrExit(sock >= 0, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(len <= OTBR_MESSAGE_SIZE, error = OT_ERROR_FAILED);
VerifyOrExit(openthread_border_router_allocate_message((void **)&req) == OT_ERROR_NONE);
VerifyOrExit(otMessageRead(aMessage, 0, req->buffer, len) == len, error = OT_ERROR_FAILED);
iov.iov_base = req->buffer;
iov.iov_len = len;
peer.sin6_family = AF_INET6;
peer.sin6_port = htons(aMessageInfo->mPeerPort);
memcpy(&peer.sin6_addr, &aMessageInfo->mPeerAddr, sizeof(otIp6Address));
if (((aMessageInfo->mPeerAddr.mFields.m8[0] == 0xfe) &&
((aMessageInfo->mPeerAddr.mFields.m8[1] & 0xc0) == 0x80)) &&
!aMessageInfo->mIsHostInterface) {
peer.sin6_scope_id = ail_iface_index;
}
msg_hdr.msg_name = &peer;
msg_hdr.msg_namelen = sizeof(peer);
msg_hdr.msg_iov = &iov;
msg_hdr.msg_iovlen = 1;
msg_hdr.msg_control = control_buf;
msg_hdr.msg_controllen = sizeof(control_buf);
msg_hdr.msg_flags = 0;
cmsg_hdr = CMSG_FIRSTHDR(&msg_hdr);
cmsg_hdr->cmsg_level = IPPROTO_IPV6;
cmsg_hdr->cmsg_type = IPV6_HOPLIMIT;
cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(int));
hop_limit = (aMessageInfo->mHopLimit ? aMessageInfo->mHopLimit : 255);
memcpy(CMSG_DATA(cmsg_hdr), &hop_limit, sizeof(int));
control_len += CMSG_SPACE(sizeof(int));
if (!otIp6IsAddressUnspecified(&aMessageInfo->mSockAddr)) {
struct in6_pktinfo pktinfo;
cmsg_hdr = CMSG_NXTHDR(&msg_hdr, cmsg_hdr);
cmsg_hdr->cmsg_level = IPPROTO_IPV6;
cmsg_hdr->cmsg_type = IPV6_PKTINFO;
cmsg_hdr->cmsg_len = CMSG_LEN(sizeof(pktinfo));
pktinfo.ipi6_ifindex = aMessageInfo->mIsHostInterface ? 0 : ail_iface_index;
memcpy(&pktinfo.ipi6_addr, &aMessageInfo->mSockAddr, sizeof(otIp6Address));
memcpy(CMSG_DATA(cmsg_hdr), &pktinfo, sizeof(pktinfo));
control_len += CMSG_SPACE(sizeof(pktinfo));
}
msg_hdr.msg_controllen = control_len;
if (aMessageInfo->mMulticastLoop) {
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on,
sizeof(on)) == 0,
error = OT_ERROR_FAILED);
}
VerifyOrExit(zsock_sendmsg(sock, &msg_hdr, 0) > 0,
error = OT_ERROR_FAILED);
otMessageFree(aMessage);
aMessage = NULL;
VerifyOrExit(zsock_getsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &optval,
&optlen) == 0,
error = OT_ERROR_FAILED);
if (optval) {
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off,
sizeof(off)) == 0,
error = OT_ERROR_FAILED);
}
exit:
if (error == OT_ERROR_NONE && aMessage) {
otMessageFree(aMessage);
}
openthread_border_router_deallocate_message((void *)req);
return error;
}
otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier,
const otIp6Address *aAddress)
{
otError error = OT_ERROR_NONE;
struct ipv6_mreq mreq = {0};
int sock;
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
memcpy(&mreq.ipv6mr_multiaddr, aAddress, sizeof(otIp6Address));
switch (aNetifIdentifier) {
case OT_NETIF_UNSPECIFIED:
break;
case OT_NETIF_THREAD_HOST:
mreq.ipv6mr_ifindex = ot_iface_index;
break;
case OT_NETIF_THREAD_INTERNAL:
assert(false);
case OT_NETIF_BACKBONE:
mreq.ipv6mr_ifindex = ail_iface_index;
break;
default:
break;
}
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
sizeof(mreq)) == 0,
error = OT_ERROR_FAILED);
exit:
return error;
}
otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier,
const otIp6Address *aAddress)
{
otError error = OT_ERROR_NONE;
struct ipv6_mreq mreq = {0};
int sock;
VerifyOrExit(aUdpSocket != NULL && aUdpSocket->mHandle != NULL,
error = OT_ERROR_INVALID_ARGS);
sock = (int)POINTER_TO_INT(aUdpSocket->mHandle);
memcpy(&mreq.ipv6mr_multiaddr, aAddress, sizeof(otIp6Address));
switch (aNetifIdentifier) {
case OT_NETIF_UNSPECIFIED:
break;
case OT_NETIF_THREAD_HOST:
mreq.ipv6mr_ifindex = ot_iface_index;
break;
case OT_NETIF_THREAD_INTERNAL:
assert(false);
case OT_NETIF_BACKBONE:
mreq.ipv6mr_ifindex = ail_iface_index;
break;
default:
break;
}
VerifyOrExit(zsock_setsockopt(sock, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq,
sizeof(mreq)) == 0,
error = OT_ERROR_FAILED);
exit:
return error;
}
static void udp_receive_handler(struct net_socket_service_event *evt)
{
uint8_t control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct in6_pktinfo))] = {0};
struct msghdr msg = {0};
struct iovec iov = {0};
struct cmsghdr *cmsg = NULL;
struct sockaddr_in6 peer_addr = {0};
struct otbr_msg_ctx *req = NULL;
ssize_t rval = 0;
VerifyOrExit(evt->event.revents & ZSOCK_POLLIN);
VerifyOrExit(openthread_border_router_allocate_message((void **)&req) == OT_ERROR_NONE);
openthread_mutex_lock();
for (otUdpSocket *ot_socket = otUdpGetSockets(ot_instance_ptr); ot_socket != NULL;
ot_socket = ot_socket->mNext) {
if ((int)POINTER_TO_INT(ot_socket->mHandle) == evt->event.fd) {
req->socket = ot_socket;
break;
}
}
openthread_mutex_unlock();
VerifyOrExit(req->socket >= 0, openthread_border_router_deallocate_message((void *)req));
iov.iov_base = req->buffer;
iov.iov_len = sizeof(req->buffer);
msg.msg_name = &peer_addr;
msg.msg_namelen = sizeof(peer_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
msg.msg_flags = 0;
rval = zsock_recvmsg(evt->event.fd, &msg, 0);
VerifyOrExit(rval > 0);
req->length = (uint16_t)rval;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6) {
if (cmsg->cmsg_type == IPV6_PKTINFO &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
struct in6_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
memcpy(&req->message_info.mSockAddr, &pktinfo.ipi6_addr,
sizeof(otIp6Address));
req->message_info.mIsHostInterface =
(pktinfo.ipi6_ifindex == (unsigned int)ail_iface_index);
} else if (cmsg->cmsg_type == IPV6_HOPLIMIT &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
int hoplimit;
memcpy(&hoplimit, CMSG_DATA(cmsg), sizeof(hoplimit));
req->message_info.mHopLimit = (uint8_t)hoplimit;
} else {
continue;
}
}
}
memcpy(&req->message_info.mPeerAddr, &peer_addr.sin6_addr, sizeof(otIp6Address));
req->message_info.mPeerPort = ntohs(peer_addr.sin6_port);
req->message_info.mSockPort = req->socket->mSockName.mPort;
openthread_border_router_post_message(req);
exit:
return;
}