1#define _POSIX_C_SOURCE 200809L
48#include <netinet/in.h>
53#include "safeapi/oal/memory/sapi_mem_util.h"
54#include "safeapi/oal/log/sapi_log.h"
55#include <sys/socket.h>
61#define POSIX_NETLINK_UDP_HELLO ((uint8_t)0xA5U)
62#define POSIX_NETLINK_UDP_HELLO_ACK ((uint8_t)0x5AU)
71#define POSIX_NETLINK_UDP_HELLO_PERIOD_MS (20U)
81typedef char posix_netlink_storage_fits_[(
sizeof(
posix_netlink_state_t) <=
sizeof(sapi_netlink_storage_t)) ? 1
84static sapi_status_t set_nonblocking(
int fd)
86 int flags = fcntl(fd, F_GETFL, 0);
90 return SAPI_STATUS_INTERNAL_ERROR;
92 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0)
94 return SAPI_STATUS_INTERNAL_ERROR;
96 return SAPI_STATUS_OK;
99static void compute_deadline(sapi_duration_ms_t timeout_ms,
struct timespec *out_deadline)
101 (void)clock_gettime(CLOCK_MONOTONIC, out_deadline);
102 out_deadline->tv_sec += (time_t)(timeout_ms / 1000U);
103 out_deadline->tv_nsec += (long)((timeout_ms % 1000U) * 1000000L);
104 if (out_deadline->tv_nsec >= 1000000000L)
106 out_deadline->tv_sec += 1;
107 out_deadline->tv_nsec -= 1000000000L;
118 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0)
122 delta_ms = ((int64_t)deadline->tv_sec - (int64_t)now.tv_sec) * 1000;
123 delta_ms += ((int64_t)deadline->tv_nsec - (int64_t)now.tv_nsec) / 1000000;
128 return (
int)delta_ms;
131static sapi_status_t open_listen_udp(
const sapi_netlink_config_t *config,
int *out_fd)
135 struct sockaddr_in bind_addr;
136 struct sockaddr_in peer_addr;
137 struct timespec deadline;
138 uint8_t ack_byte = POSIX_NETLINK_UDP_HELLO_ACK;
140 fd = socket(AF_INET, SOCK_DGRAM, 0);
143 return SAPI_STATUS_INTERNAL_ERROR;
145 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
147 sapi_mem_set(&bind_addr, 0,
sizeof(bind_addr));
148 bind_addr.sin_family = AF_INET;
149 bind_addr.sin_port = htons(config->port);
150 if (config->host == NULL)
152 bind_addr.sin_addr.s_addr = INADDR_ANY;
154 else if (inet_pton(AF_INET, config->host, &bind_addr.sin_addr) != 1)
157 return SAPI_STATUS_INVALID_PARAM;
160 if (bind(fd, (
struct sockaddr *)&bind_addr,
sizeof(bind_addr)) != 0)
164 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: bind port %u failed errno=%d", config->port, errno);
165 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
167 return SAPI_STATUS_INTERNAL_ERROR;
172 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: bound port %u successfully (fd=%d)", config->port, fd);
173 sapi_log_write(SAPI_LOG_LEVEL_INFO,
"NETLINK", msg);
175 if (set_nonblocking(fd) != SAPI_STATUS_OK)
178 return SAPI_STATUS_INTERNAL_ERROR;
181 compute_deadline(config->connect_timeout_ms, &deadline);
189 socklen_t peer_addr_len =
sizeof(peer_addr);
194 rc = poll(&pfd, 1, (wait_ms > 0) ? wait_ms : 0);
198 return SAPI_STATUS_TIMEOUT;
207 return SAPI_STATUS_TIMEOUT;
212 return SAPI_STATUS_INTERNAL_ERROR;
215 sapi_mem_set(&peer_addr, 0,
sizeof(peer_addr));
216 n = recvfrom(fd, &hello_byte,
sizeof(hello_byte), 0, (
struct sockaddr *)&peer_addr, &peer_addr_len);
219 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
226 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: recvfrom failed errno=%d", errno);
227 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
230 return SAPI_STATUS_INTERNAL_ERROR;
236 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: unexpected byte %02x, n=%ld", hello_byte, (
long)n);
237 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK", msg);
241 if (connect(fd, (
struct sockaddr *)&peer_addr, peer_addr_len) != 0)
245 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: connect failed errno=%d", errno);
246 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
248 return SAPI_STATUS_INTERNAL_ERROR;
250 sapi_log_write(SAPI_LOG_LEVEL_INFO,
"NETLINK",
"open_listen_udp: connected to peer, sending ACK");
254 if (send(fd, &ack_byte,
sizeof(ack_byte), 0) != (ssize_t)
sizeof(ack_byte))
258 (void)snprintf(msg,
sizeof(msg),
"open_listen_udp: send ACK failed errno=%d", errno);
259 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
261 return SAPI_STATUS_INTERNAL_ERROR;
263 sapi_log_write(SAPI_LOG_LEVEL_INFO,
"NETLINK",
"open_listen_udp: ACK sent OK");
276 return SAPI_STATUS_OK;
279static sapi_status_t open_connect_udp(
const sapi_netlink_config_t *config,
int *out_fd)
281 struct addrinfo hints;
282 struct addrinfo *resolved = NULL;
285 struct timespec deadline;
286 struct timespec next_hello_deadline;
289 (void)snprintf(port_str,
sizeof(port_str),
"%u", (
unsigned int)config->port);
293 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: entry host=%s port=%s", config->host, port_str);
294 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK", msg);
297 sapi_mem_set(&hints, 0,
sizeof(hints));
298 hints.ai_family = AF_INET;
299 hints.ai_socktype = SOCK_DGRAM;
300 if (getaddrinfo(config->host, port_str, &hints, &resolved) != 0)
302 return SAPI_STATUS_INVALID_PARAM;
305 fd = socket(resolved->ai_family, resolved->ai_socktype, resolved->ai_protocol);
308 freeaddrinfo(resolved);
309 return SAPI_STATUS_INTERNAL_ERROR;
311 if (set_nonblocking(fd) != SAPI_STATUS_OK)
314 freeaddrinfo(resolved);
315 return SAPI_STATUS_INTERNAL_ERROR;
322 if (connect(fd, resolved->ai_addr, resolved->ai_addrlen) != 0)
326 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: connect failed errno=%d", errno);
327 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
329 freeaddrinfo(resolved);
330 return SAPI_STATUS_INTERNAL_ERROR;
335 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: connect succeeded (fd=%d)", fd);
336 sapi_log_write(SAPI_LOG_LEVEL_INFO,
"NETLINK", msg);
338 freeaddrinfo(resolved);
340 compute_deadline(config->connect_timeout_ms, &deadline);
341 compute_deadline(0U, &next_hello_deadline);
342 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK",
"open_connect_udp: entering loop");
366 return SAPI_STATUS_TIMEOUT;
370 if (hello_wait_ms <= 0)
372 ssize_t sres = send(fd, &hello_byte,
sizeof(hello_byte), 0);
374 if ((sres < 0) && (errno != ECONNREFUSED) &&
375 (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != ENOTCONN) &&
376 (errno != EHOSTUNREACH) && (errno != EADDRNOTAVAIL) && (errno != EPIPE) &&
377 (errno != ECONNRESET) && (errno != ENETUNREACH))
381 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: unhandled send error errno=%d", errno);
382 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
384 return SAPI_STATUS_INTERNAL_ERROR;
390 poll_wait_ms = (hello_wait_ms < wait_ms) ? hello_wait_ms : wait_ms;
394 rc = poll(&pfd, 1, poll_wait_ms);
402 return SAPI_STATUS_INTERNAL_ERROR;
409 n = recv(fd, &ack_byte,
sizeof(ack_byte), MSG_PEEK);
412 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
413 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EPIPE) || (errno == ECONNRESET) ||
414 (errno == ENETUNREACH))
421 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: recv failed errno=%d", errno);
422 sapi_log_write(SAPI_LOG_LEVEL_ERROR,
"NETLINK", msg);
425 return SAPI_STATUS_INTERNAL_ERROR;
430 (void)snprintf(msg,
sizeof(msg),
"open_connect_udp: peeked n=%ld byte=%02x", (
long)n, ack_byte);
431 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK", msg);
433 if ((n == (ssize_t)
sizeof(ack_byte)) && (ack_byte == POSIX_NETLINK_UDP_HELLO_ACK))
435 uint8_t consumed_byte;
437 (void)recv(fd, &consumed_byte,
sizeof(consumed_byte), 0);
438 sapi_log_write(SAPI_LOG_LEVEL_INFO,
"NETLINK",
"open_connect_udp: consumed ACK successfully");
448 return SAPI_STATUS_OK;
451static sapi_status_t backend_open(sapi_netlink_storage_t *storage,
const sapi_netlink_config_t *config,
452 sapi_netlink_handle_t *out_handle)
456 sapi_status_t status;
458 if ((storage == NULL) || (config == NULL) || (out_handle == NULL))
460 return SAPI_STATUS_INVALID_PARAM;
466 (void)snprintf(msg,
sizeof(msg),
"backend_open: role=%d host=%s port=%u", config->role,
467 config->host ? config->host :
"NULL", config->port);
468 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK", msg);
471 (config->role == SAPI_NETLINK_ROLE_LISTEN) ? open_listen_udp(config, &fd) : open_connect_udp(config, &fd);
475 (void)snprintf(msg,
sizeof(msg),
"backend_open (role=%d): returned status=%d", config->role, (
int)status);
476 sapi_log_write(SAPI_LOG_LEVEL_DEBUG,
"NETLINK", msg);
478 if (status != SAPI_STATUS_OK)
485 state->message_size = config->message_size;
486 *out_handle = (sapi_netlink_handle_t)(
void *)storage;
487 return SAPI_STATUS_OK;
490static sapi_status_t backend_send(sapi_netlink_handle_t handle,
const void *message,
size_t message_size,
491 sapi_duration_ms_t timeout_ms)
494 struct timespec deadline;
496 if ((state == NULL) || (message == NULL) || (message_size == 0U))
498 return SAPI_STATUS_INVALID_PARAM;
501 compute_deadline(timeout_ms, &deadline);
511 return SAPI_STATUS_TIMEOUT;
514 pfd.events = POLLOUT;
516 rc = poll(&pfd, 1, wait_ms);
519 return SAPI_STATUS_TIMEOUT;
527 return SAPI_STATUS_INTERNAL_ERROR;
530 n = send(state->fd, message, message_size, 0);
533 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
537 if (errno == ECONNREFUSED)
539 return SAPI_STATUS_HARDWARE_FAULT;
541 return SAPI_STATUS_INTERNAL_ERROR;
543 if ((
size_t)n != message_size)
549 return SAPI_STATUS_INTERNAL_ERROR;
551 return SAPI_STATUS_OK;
555static sapi_status_t backend_receive(sapi_netlink_handle_t handle,
void *out_message,
size_t buffer_size,
556 sapi_duration_ms_t timeout_ms)
559 struct timespec deadline;
561 if ((state == NULL) || (out_message == NULL) || (buffer_size == 0U))
563 return SAPI_STATUS_INVALID_PARAM;
566 compute_deadline(timeout_ms, &deadline);
576 return SAPI_STATUS_TIMEOUT;
581 rc = poll(&pfd, 1, wait_ms);
584 return SAPI_STATUS_TIMEOUT;
592 return SAPI_STATUS_INTERNAL_ERROR;
600 n = recv(state->fd, out_message, buffer_size, 0);
602 n = recv(state->fd, out_message, buffer_size, MSG_TRUNC);
606 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
610 if (errno == ECONNREFUSED)
612 return SAPI_STATUS_HARDWARE_FAULT;
614 return SAPI_STATUS_INTERNAL_ERROR;
616 if ((
size_t)n != buffer_size)
626 if ((n == (ssize_t)
sizeof(uint8_t)) &&
628 (((
const uint8_t *)out_message)[0] == POSIX_NETLINK_UDP_HELLO_ACK)))
637 return SAPI_STATUS_DATA_CORRUPTION;
639 return SAPI_STATUS_OK;
643static sapi_status_t backend_close(sapi_netlink_handle_t handle)
649 return SAPI_STATUS_INVALID_PARAM;
651 (void)close(state->fd);
652 return SAPI_STATUS_OK;
655static const sapi_netlink_backend_t s_posix_netlink_backend = { backend_open, backend_send, backend_receive,
660 return &s_posix_netlink_backend;
Real POSIX/Linux backend for every OAL service (ADR-018).
const sapi_netlink_backend_t * sapi_posix_backend_netlink(void)
The POSIX sapi_netlink backend (TCP sockets, LISTEN/CONNECT).
static int remaining_ms(const struct timespec *deadline)
#define POSIX_NETLINK_UDP_HELLO
#define POSIX_NETLINK_UDP_HELLO_PERIOD_MS