fumi/core/shim/udp_interface.cpp

199 lines
5.1 KiB
C++
Raw Normal View History

2026-09-28 16:49:34 +03:00
#include "udp_interface.h"
#include <microReticulum/Transport.h>
#include <microReticulum/Log.h>
#ifndef ARDUINO
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#endif
using namespace RNS;
UDPInterface::UDPInterface(const char* name,
const std::string& local_host, int local_port,
const std::string& remote_host, int remote_port)
: RNS::InterfaceImpl(name) {
_IN = true;
_OUT = true;
_bitrate = BITRATE_GUESS;
_HW_MTU = 1064;
_local_host = local_host;
_local_port = local_port;
if (!remote_host.empty()) {
_forward_configured = true;
_remote_host = remote_host;
_remote_port = remote_port;
}
}
/*virtual*/ UDPInterface::~UDPInterface() {
stop();
}
/*virtual*/ bool UDPInterface::start() {
_online = false;
#ifdef ARDUINO
udp.begin(_local_port);
#else
// resolve local host
struct in_addr local_addr;
if (inet_aton(_local_host.c_str(), &local_addr) == 0) {
struct hostent* host_ent = gethostbyname(_local_host.c_str());
if (host_ent == nullptr || host_ent->h_addr_list[0] == nullptr) {
ERRORF("Unable to resolve local host %s", _local_host.c_str());
return false;
}
_local_address = *((in_addr_t*)(host_ent->h_addr_list[0]));
}
else {
_local_address = local_addr.s_addr;
}
_remote_address = INADDR_NONE;
if (_forward_configured) {
struct in_addr remote_addr;
if (inet_aton(_remote_host.c_str(), &remote_addr) == 0) {
struct hostent* host_ent = gethostbyname(_remote_host.c_str());
if (host_ent == nullptr || host_ent->h_addr_list[0] == nullptr) {
ERRORF("Unable to resolve remote host %s", _remote_host.c_str());
return false;
}
_remote_address = *((in_addr_t*)(host_ent->h_addr_list[0]));
}
else {
_remote_address = remote_addr.s_addr;
}
}
_socket = socket( PF_INET, SOCK_DGRAM, 0 );
if (_socket < 0) {
ERRORF("Unable to create socket with error %d", errno);
return false;
}
int broadcast = 1;
setsockopt(_socket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
int reuse = 1;
setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
#ifdef SO_REUSEPORT
setsockopt(_socket, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
#endif
INFOF("Binding UDP socket %d to %s:%d", _socket, _local_host.c_str(), _local_port);
sockaddr_in bind_addr;
memset(&bind_addr, 0, sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_addr.s_addr = _local_address;
bind_addr.sin_port = htons(_local_port);
if (bind(_socket, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1) {
ERRORF("Unable to bind socket with error %d", errno);
close(_socket);
_socket = -1;
return false;
}
#endif
_online = true;
return true;
}
/*virtual*/ void UDPInterface::stop() {
#ifndef ARDUINO
if (_socket > -1) {
close(_socket);
_socket = -1;
}
#endif
_online = false;
}
/*virtual*/ void UDPInterface::loop() {
if (!_online) {
return;
}
#ifdef ARDUINO
udp.parsePacket();
size_t len = udp.read(_buffer.writable(Type::Reticulum::MTU), Type::Reticulum::MTU);
if (len > 0) {
_buffer.resize(len);
on_incoming(_buffer);
}
#else
// One datagram per recvfrom() with MSG_DONTWAIT, looping until the
// kernel queue is empty — the same drain pattern as the microReticulum
// example, which avoids stale/zero FIONREAD counts on some platforms.
while (true) {
sockaddr_in src_addr{};
socklen_t src_addr_len = sizeof(src_addr);
ssize_t len = recvfrom(_socket,
_buffer.writable(_HW_MTU),
_HW_MTU,
MSG_DONTWAIT,
(struct sockaddr*)&src_addr,
&src_addr_len);
if (len <= 0) {
break;
}
_buffer.resize(static_cast<size_t>(len));
if (!_forward_configured) {
_last_src_addr = src_addr;
_have_src = true;
}
on_incoming(_buffer);
}
#endif
}
/*virtual*/ bool UDPInterface::send_outgoing(const RNS::Bytes& data) {
bool success = true;
try {
if (_online) {
#ifdef ARDUINO
udp.beginPacket(_remote_host.c_str(), _remote_port);
udp.write(data.data(), data.size());
udp.endPacket();
#else
sockaddr_in sock_addr;
if (_forward_configured) {
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = _remote_address;
sock_addr.sin_port = htons(_remote_port);
}
else if (_have_src) {
// No forward target: reply to whoever spoke to us last.
sock_addr = _last_src_addr;
}
else {
WARNING("UDPInterface: no forward target and no peer heard from yet, dropping outgoing packet");
return false;
}
ssize_t sent = sendto(_socket, data.data(), data.size(), 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
if (sent != (ssize_t)data.size()) {
WARNINGF("Failed sending %d bytes via UDP", (int)data.size());
success = false;
}
#endif
}
InterfaceImpl::handle_outgoing(data);
}
catch (const std::exception& e) {
ERRORF("Could not transmit on %s. The contained exception was: %s", toString().c_str(), e.what());
success = false;
}
return success;
}
void UDPInterface::on_incoming(const RNS::Bytes& data) {
InterfaceImpl::handle_incoming(data);
}