RTE OSAdapter POSIX 0.1.0 build 41b3eb3
POSIX/Linux OAL OSAdapter for Platform_RTE
Loading...
Searching...
No Matches
rte_posix_osadapter_netlink.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
60
61#include <arpa/inet.h>
62#include <errno.h>
63#include <fcntl.h>
64#include <netdb.h>
65#include <netinet/in.h>
66#include <poll.h>
67#include <stdbool.h>
68#include <stdint.h>
69#include <stdio.h>
70#include "rte/oal/memory/rte_mem_util.h"
71#include "rte/oal/log/rte_log.h"
72#include <sys/socket.h>
73#include <time.h>
74#include <unistd.h>
75
78#define POSIX_NETLINK_UDP_HELLO ((uint8_t)0xA5U)
79#define POSIX_NETLINK_UDP_HELLO_ACK ((uint8_t)0x5AU)
88#define POSIX_NETLINK_UDP_HELLO_PERIOD_MS (20U)
89
90typedef struct
91{
92 int fd;
93 size_t message_size;
94 bool listen;
95 socklen_t peer_len;
96 struct sockaddr_in peer;
98
99/* C99-compatible static assert: posix_netlink_state_t must fit inside
100 * rte_netlink_storage_t's reserved bytes. */
101typedef char posix_netlink_storage_fits_[(sizeof(posix_netlink_state_t) <= sizeof(rte_netlink_storage_t)) ? 1
102 : -1];
103
104static rte_status_t set_nonblocking(int fd)
105{
106 int flags = fcntl(fd, F_GETFL, 0);
107
108 if (flags < 0)
109 {
110 return RTE_STATUS_INTERNAL_ERROR;
111 }
112 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0)
113 {
114 return RTE_STATUS_INTERNAL_ERROR;
115 }
116 return RTE_STATUS_OK;
117}
118
119static void compute_deadline(rte_duration_ms_t timeout_ms, struct timespec *out_deadline)
120{
121 (void)clock_gettime(CLOCK_MONOTONIC, out_deadline);
122 out_deadline->tv_sec += (time_t)(timeout_ms / 1000U);
123 out_deadline->tv_nsec += (long)((timeout_ms % 1000U) * 1000000L);
124 if (out_deadline->tv_nsec >= 1000000000L)
125 {
126 out_deadline->tv_sec += 1;
127 out_deadline->tv_nsec -= 1000000000L;
128 }
129}
130
132static bool same_peer(const struct sockaddr_in *a, const struct sockaddr_in *b)
133{
134 return (a->sin_addr.s_addr == b->sin_addr.s_addr) && (a->sin_port == b->sin_port);
135}
136
137static rte_status_t open_listen_udp(const rte_netlink_config_t *config, int *out_fd, struct sockaddr_in *out_peer,
138 socklen_t *out_peer_len)
139{
140 int fd;
141 int reuse = 1;
142 struct sockaddr_in bind_addr;
143 struct sockaddr_in peer_addr;
144 struct timespec deadline;
145 uint8_t ack_byte = POSIX_NETLINK_UDP_HELLO_ACK;
146 socklen_t peer_addr_len = sizeof(peer_addr);
147
148 fd = socket(AF_INET, SOCK_DGRAM, 0);
149 if (fd >= 0)
150 {
151 /* Not inherited across execv(): the in-process REBOOT re-execs this binary, and an inherited listening socket
152 * kept receiving datagrams for its port that nobody read (Docker: sockets piled up with every reboot). */
153 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
154 }
155 if (fd < 0)
156 {
157 return RTE_STATUS_INTERNAL_ERROR;
158 }
159 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
160
161 rte_mem_set(&bind_addr, 0, sizeof(bind_addr));
162 bind_addr.sin_family = AF_INET;
163 bind_addr.sin_port = htons(config->port);
164 if (config->host == NULL)
165 {
166 bind_addr.sin_addr.s_addr = INADDR_ANY;
167 }
168 else if (inet_pton(AF_INET, config->host, &bind_addr.sin_addr) != 1)
169 {
170 (void)close(fd);
171 return RTE_STATUS_INVALID_PARAM;
172 }
173
174 if (bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0)
175 {
176 char msg[80];
177
178 (void)snprintf(msg, sizeof(msg), "RTE: open_listen_udp: bind port %u failed errno=%d", config->port, errno);
179 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
180 (void)close(fd);
181 return RTE_STATUS_INTERNAL_ERROR;
182 }
183 {
184 char msg[80];
185
186 (void)snprintf(msg, sizeof(msg), "RTE: open UDP listen bound port %u successfully (fd=%d)", config->port, fd);
187 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", msg);
188 }
189 /* REQ-OAL-NETLINK-015: configured buffer sizes before the handshake; both 0 -> no call at all. */
190 {
191 const rte_status_t buf_status = posix_socket_apply_buffers(fd, config);
192
193 if (buf_status != RTE_STATUS_OK)
194 {
195 (void)close(fd);
196 return buf_status;
197 }
198 }
199 if (set_nonblocking(fd) != RTE_STATUS_OK)
200 {
201 (void)close(fd);
202 return RTE_STATUS_INTERNAL_ERROR;
203 }
204
205 compute_deadline(config->connect_timeout_ms, &deadline);
206 for (;;)
207 {
208 struct pollfd pfd;
209 int wait_ms = posix_remaining_ms(&deadline);
210 int rc;
211 ssize_t n;
212 uint8_t hello_byte;
213
214 pfd.fd = fd;
215 pfd.events = POLLIN;
216 pfd.revents = 0;
217 rc = poll(&pfd, 1, (wait_ms > 0) ? wait_ms : 0);
218 if (rc == 0)
219 {
220 (void)close(fd);
221 return RTE_STATUS_TIMEOUT;
222 }
223 if (rc < 0)
224 {
225 if (errno == EINTR)
226 {
227 if (wait_ms <= 0)
228 {
229 (void)close(fd);
230 return RTE_STATUS_TIMEOUT;
231 }
232 continue;
233 }
234 (void)close(fd);
235 return RTE_STATUS_INTERNAL_ERROR;
236 }
237
238 rte_mem_set(&peer_addr, 0, sizeof(peer_addr));
239 peer_addr_len = sizeof(peer_addr);
240 n = recvfrom(fd, &hello_byte, sizeof(hello_byte), 0, (struct sockaddr *)&peer_addr, &peer_addr_len);
241 if (n < 0)
242 {
243 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
244 {
245 continue;
246 }
247 {
248 char msg[64];
249
250 (void)snprintf(msg, sizeof(msg), "RTE: open_listen_udp: recvfrom failed errno=%d", errno);
251 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
252 }
253 (void)close(fd);
254 return RTE_STATUS_INTERNAL_ERROR;
255 }
256 /* Any datagram identifies the peer, not only a HELLO: when THIS side restarted while the peer kept running,
257 * the peer is already connected and never sends another HELLO, only its ordinary frames (that one frame is
258 * dropped here). Requiring a HELLO left a restarted listener waiting forever. */
259 (void)hello_byte;
260 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", "RTE: open UDP listen learned its peer, sending ACK");
261 break;
262 }
263
264 if (sendto(fd, &ack_byte, sizeof(ack_byte), 0, (struct sockaddr *)&peer_addr, peer_addr_len)
265 != (ssize_t)sizeof(ack_byte))
266 {
267 char msg[64];
268
269 (void)snprintf(msg, sizeof(msg), "RTE: open_listen_udp: send ACK failed errno=%d", errno);
270 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
271 (void)close(fd);
272 return RTE_STATUS_INTERNAL_ERROR;
273 }
274 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", "RTE: open UDP listen ACK sent OK");
275
276 /* Deliberately no post-ACK "grace drain" wait here: an earlier version
277 * of this function blocked open() for a further fixed window to
278 * absorb any HELLO retransmits still in flight from the peer, but
279 * that unconditionally delayed every LISTEN-role open() (and, in
280 * turn, callers with a tight end-to-end budget - e.g.
281 * RTE_EXAMPLE_AB_CHECKPOINT_MAX_DELAY_MS - built on the old TCP
282 * OSAdapter's much faster connection establishment). A stray duplicate
283 * HELLO that arrives after open() has already returned is instead
284 * handled cheaply, on demand, inside backend_receive() below - no
285 * fixed delay paid unless one actually shows up. */
286 *out_fd = fd;
287 *out_peer = peer_addr;
288 *out_peer_len = peer_addr_len;
289 return RTE_STATUS_OK;
290}
291
292static rte_status_t open_connect_udp(const rte_netlink_config_t *config, int *out_fd)
293{
294 struct addrinfo hints;
295 struct addrinfo *resolved = NULL;
296 char port_str[8];
297 int fd;
298 struct timespec deadline;
299 struct timespec next_hello_deadline;
300 uint8_t hello_byte = POSIX_NETLINK_UDP_HELLO;
301
302 (void)snprintf(port_str, sizeof(port_str), "%u", (unsigned int)config->port);
303 {
304 char msg[96];
305
306 (void)snprintf(msg, sizeof(msg), "RTE: opening UDP connection to host=%s port=%s", config->host, port_str);
307 rte_log_write(RTE_LOG_LEVEL_DEBUG, "RTE", msg);
308 }
309
310 rte_mem_set(&hints, 0, sizeof(hints));
311 hints.ai_family = AF_INET;
312 hints.ai_socktype = SOCK_DGRAM;
313 if (getaddrinfo(config->host, port_str, &hints, &resolved) != 0)
314 {
315 return RTE_STATUS_INVALID_PARAM;
316 }
317
318 fd = socket(resolved->ai_family, resolved->ai_socktype, resolved->ai_protocol);
319 if (fd >= 0)
320 {
321 /* Not inherited across execv(): the in-process REBOOT re-execs this binary, and an inherited listening socket
322 * kept receiving datagrams for its port that nobody read (Docker: sockets piled up with every reboot). */
323 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
324 }
325 if (fd < 0)
326 {
327 freeaddrinfo(resolved);
328 return RTE_STATUS_INTERNAL_ERROR;
329 }
330 /* REQ-OAL-NETLINK-015: configured buffer sizes before the handshake; both 0 -> no call at all. */
331 {
332 const rte_status_t buf_status = posix_socket_apply_buffers(fd, config);
333
334 if (buf_status != RTE_STATUS_OK)
335 {
336 (void)close(fd);
337 freeaddrinfo(resolved);
338 return buf_status;
339 }
340 }
341 if (set_nonblocking(fd) != RTE_STATUS_OK)
342 {
343 (void)close(fd);
344 freeaddrinfo(resolved);
345 return RTE_STATUS_INTERNAL_ERROR;
346 }
347
348 /* connect() on a UDP socket only fixes the default peer address
349 * locally - it performs no network I/O and gives no signal that the
350 * peer is actually reachable, unlike TCP's connect(). The HELLO/
351 * HELLO_ACK exchange below is what makes RTE_STATUS_OK meaningful. */
352 if (connect(fd, resolved->ai_addr, resolved->ai_addrlen) != 0)
353 {
354 char msg[64];
355
356 (void)snprintf(msg, sizeof(msg), "RTE: open UDP connect failed errno=%d", errno);
357 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
358 (void)close(fd);
359 freeaddrinfo(resolved);
360 return RTE_STATUS_INTERNAL_ERROR;
361 }
362 {
363 char msg[64];
364
365 (void)snprintf(msg, sizeof(msg), "RTE: open UDP connect succeeded (fd=%d)", fd);
366 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", msg);
367 }
368 freeaddrinfo(resolved);
369
370 compute_deadline(config->connect_timeout_ms, &deadline);
371 compute_deadline(0U, &next_hello_deadline); /* send the first HELLO immediately */
372 rte_log_write(RTE_LOG_LEVEL_DEBUG, "RTE", "RTE: open UDP handshake entering loop");
373 for (;;)
374 {
375 struct pollfd pfd;
376 int wait_ms = posix_remaining_ms(&deadline);
377 int hello_wait_ms;
378 int poll_wait_ms;
379 int rc;
380 ssize_t n;
381 uint8_t ack_byte;
382
383 /* Deliberately no per-iteration log line here: this loop's own
384 * HELLO retry cadence (POSIX_NETLINK_UDP_HELLO_PERIOD_MS, 20ms)
385 * means a per-iteration DEBUG line fires roughly 50x/second for
386 * as long as the peer isn't up yet - real overhead during a
387 * connection-establishment window that later turned out to
388 * matter for a known-fragile startup negotiation race
389 * (ab_gp_channel_crosscompare.c's NEGOTIATION MISMATCH ->
390 * REBOOT path, common_config.h's own ADR-029 comment on it) -
391 * only genuinely exceptional outcomes (an unhandled send error
392 * below) are worth a log line at this cadence. */
393 if (wait_ms <= 0)
394 {
395 (void)close(fd);
396 return RTE_STATUS_TIMEOUT;
397 }
398
399 hello_wait_ms = posix_remaining_ms(&next_hello_deadline);
400 if (hello_wait_ms <= 0)
401 {
402 ssize_t sres = send(fd, &hello_byte, sizeof(hello_byte), 0);
403
404 if ((sres < 0) && (errno != ECONNREFUSED) &&
405 (errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != ENOTCONN) &&
406 (errno != EHOSTUNREACH) && (errno != EADDRNOTAVAIL) && (errno != EPIPE) &&
407 (errno != ECONNRESET) && (errno != ENETUNREACH))
408 {
409 char msg[64];
410
411 (void)snprintf(msg, sizeof(msg), "RTE: open_connect_udp: unhandled send error errno=%d", errno);
412 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
413 (void)close(fd);
414 return RTE_STATUS_INTERNAL_ERROR;
415 }
416 compute_deadline((rte_duration_ms_t)POSIX_NETLINK_UDP_HELLO_PERIOD_MS, &next_hello_deadline);
417 hello_wait_ms = posix_remaining_ms(&next_hello_deadline);
418 }
419
420 poll_wait_ms = (hello_wait_ms < wait_ms) ? hello_wait_ms : wait_ms;
421 pfd.fd = fd;
422 pfd.events = POLLIN;
423 pfd.revents = 0;
424 rc = poll(&pfd, 1, poll_wait_ms);
425 if (rc < 0)
426 {
427 if (errno == EINTR)
428 {
429 continue;
430 }
431 (void)close(fd);
432 return RTE_STATUS_INTERNAL_ERROR;
433 }
434 if (rc == 0)
435 {
436 continue; /* hello-resend tick or spurious wake - re-evaluate both deadlines */
437 }
438
439 n = recv(fd, &ack_byte, sizeof(ack_byte), MSG_PEEK);
440 if (n < 0)
441 {
442 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
443 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EPIPE) || (errno == ECONNRESET) ||
444 (errno == ENETUNREACH))
445 {
446 continue; /* including ECONNREFUSED/ENOTCONN/ECONNRESET: peer not up yet */
447 }
448 {
449 char msg[64];
450
451 (void)snprintf(msg, sizeof(msg), "RTE: open_connect_udp: recv failed errno=%d", errno);
452 rte_log_write(RTE_LOG_LEVEL_ERROR, "RTE", msg);
453 }
454 (void)close(fd);
455 return RTE_STATUS_INTERNAL_ERROR;
456 }
457 {
458 char msg[64];
459
460 (void)snprintf(msg, sizeof(msg), "RTE: open_connect_udp: peeked n=%ld byte=%02x", (long)n, ack_byte);
461 rte_log_write(RTE_LOG_LEVEL_DEBUG, "RTE", msg);
462 }
463 if ((n == (ssize_t)sizeof(ack_byte)) && (ack_byte == POSIX_NETLINK_UDP_HELLO_ACK))
464 {
465 uint8_t consumed_byte;
466
467 (void)recv(fd, &consumed_byte, sizeof(consumed_byte), 0); /* actually consume the ACK */
468 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", "RTE: open UDP connect consumed ACK successfully");
469 break;
470 }
471 /* Not our handshake ACK (or a duplicate HELLO we ourselves sent
472 * echoing back is impossible - this is a unidirectional wait) -
473 * leave it queued for the caller's first real receive() and keep
474 * waiting within the same deadline. */
475 }
476
477 *out_fd = fd;
478 return RTE_STATUS_OK;
479}
480
481static rte_status_t osadapter_open(rte_netlink_storage_t *storage, const rte_netlink_config_t *config,
482 rte_netlink_handle_t *out_handle)
483{
485 int fd = -1;
486 rte_status_t status;
487 struct sockaddr_in peer;
488 socklen_t peer_len = 0;
489
490 if ((storage == NULL) || (config == NULL) || (out_handle == NULL))
491 {
492 return RTE_STATUS_INVALID_PARAM;
493 }
494
495 {
496 char msg[96];
497
498 (void)snprintf(msg, sizeof(msg), "RTE: osadapter_open role=%d host=%s port=%u", config->role,
499 config->host ? config->host : "NULL", config->port);
500 rte_log_write(RTE_LOG_LEVEL_DEBUG, "RTE", msg);
501 }
502 rte_mem_set(&peer, 0, sizeof(peer));
503 if (config->role == RTE_NETLINK_ROLE_LISTEN)
504 {
505 status = open_listen_udp(config, &fd, &peer, &peer_len);
506 }
507 else
508 {
509 status = open_connect_udp(config, &fd);
510 }
511 {
512 char msg[64];
513
514 (void)snprintf(msg, sizeof(msg), "RTE: osadapter_open (role=%d) returned status=%d", config->role, (int)status);
515 rte_log_write(RTE_LOG_LEVEL_DEBUG, "RTE", msg);
516 }
517 if (status != RTE_STATUS_OK)
518 {
519 return status;
520 }
521
522 state = (posix_netlink_state_t *)(void *)storage;
523 state->fd = fd;
524 state->message_size = config->message_size;
525 state->listen = (config->role == RTE_NETLINK_ROLE_LISTEN);
526 state->peer = peer;
527 state->peer_len = peer_len;
528 *out_handle = (rte_netlink_handle_t)(void *)storage;
529 return RTE_STATUS_OK;
530}
531
532static rte_status_t backend_send(rte_netlink_handle_t handle, const void *message, size_t message_size,
533 rte_duration_ms_t timeout_ms)
534{
535 posix_netlink_state_t *state = (posix_netlink_state_t *)(void *)handle;
536 struct timespec deadline;
537 bool first_iteration = true;
538
539 if ((state == NULL) || (message == NULL) || (message_size == 0U))
540 {
541 return RTE_STATUS_INVALID_PARAM;
542 }
543
544 compute_deadline(timeout_ms, &deadline);
545 for (;;)
546 {
547 struct pollfd pfd;
548 int wait_ms = posix_remaining_ms(&deadline);
549 int rc;
550 ssize_t n;
551
552 if (!first_iteration && (wait_ms <= 0))
553 {
554 return RTE_STATUS_TIMEOUT;
555 }
556 first_iteration = false;
557 if (wait_ms < 0)
558 {
559 wait_ms = 0;
560 }
561 pfd.fd = state->fd;
562 pfd.events = POLLOUT;
563 pfd.revents = 0;
564 rc = poll(&pfd, 1, wait_ms);
565 if (rc == 0)
566 {
567 return RTE_STATUS_TIMEOUT;
568 }
569 if (rc < 0)
570 {
571 if (errno == EINTR)
572 {
573 continue;
574 }
575 return RTE_STATUS_INTERNAL_ERROR;
576 }
577
578 n = state->listen
579 ? sendto(state->fd, message, message_size, 0, (const struct sockaddr *)&state->peer, state->peer_len)
580 : send(state->fd, message, message_size, 0);
581 if (n < 0)
582 {
583 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
584 {
585 continue;
586 }
587 if (errno == ECONNREFUSED)
588 {
589 return RTE_STATUS_HARDWARE_FAULT;
590 }
591 return RTE_STATUS_INTERNAL_ERROR;
592 }
593 if ((size_t)n != message_size)
594 {
595 /* A connected SOCK_DGRAM send() is documented atomic-or-fails;
596 * a short write here means an unexpected kernel condition, not
597 * a partial transfer to resume (there is nothing to loop for,
598 * unlike a TCP stream). */
599 return RTE_STATUS_INTERNAL_ERROR;
600 }
601 return RTE_STATUS_OK;
602 }
603}
604
605static rte_status_t backend_receive(rte_netlink_handle_t handle, void *out_message, size_t buffer_size,
606 rte_duration_ms_t timeout_ms)
607{
608 posix_netlink_state_t *state = (posix_netlink_state_t *)(void *)handle;
609 struct timespec deadline;
610 bool first_iteration = true;
611
612 if ((state == NULL) || (out_message == NULL) || (buffer_size == 0U))
613 {
614 return RTE_STATUS_INVALID_PARAM;
615 }
616
617 compute_deadline(timeout_ms, &deadline);
618 for (;;)
619 {
620 struct pollfd pfd;
621 int wait_ms = posix_remaining_ms(&deadline);
622 int rc;
623 ssize_t n;
624
625 if (!first_iteration && (wait_ms <= 0))
626 {
627 return RTE_STATUS_TIMEOUT;
628 }
629 first_iteration = false;
630 if (wait_ms < 0)
631 {
632 wait_ms = 0;
633 }
634 pfd.fd = state->fd;
635 pfd.events = POLLIN;
636 pfd.revents = 0;
637 rc = poll(&pfd, 1, wait_ms);
638 if (rc == 0)
639 {
640 return RTE_STATUS_TIMEOUT;
641 }
642 if (rc < 0)
643 {
644 if (errno == EINTR)
645 {
646 continue;
647 }
648 return RTE_STATUS_INTERNAL_ERROR;
649 }
650
651 /* Linux: MSG_TRUNC makes the kernel report the datagram's real
652 * length even if it exceeded buffer_size, so the length check
653 * below rejects an oversized datagram as well as an undersized
654 * one. Darwin does not honour MSG_TRUNC as a recvfrom() input
655 * flag, so flags are 0 there: an oversized datagram is silently
656 * truncated to buffer_size and accepted as a full-size frame
657 * (RTE_STATUS_OK) - only an undersized one is detected. */
658 {
659 struct sockaddr_in src;
660 socklen_t src_len = sizeof(src);
661#ifdef __APPLE__
662 n = recvfrom(state->fd, out_message, buffer_size, 0, (struct sockaddr *)&src, &src_len);
663#else
664 n = recvfrom(state->fd, out_message, buffer_size, MSG_TRUNC, (struct sockaddr *)&src, &src_len);
665#endif
666 if ((n >= 0) && state->listen && !same_peer(&src, &state->peer))
667 {
668 /* LISTEN role, datagram from a source other than the learned peer. A HELLO means the peer restarted
669 * (new source port): re-learn it and answer, so the link recovers. Anything else is a stray. */
670 if ((n == (ssize_t)sizeof(uint8_t)) && (((const uint8_t *)out_message)[0] == POSIX_NETLINK_UDP_HELLO))
671 {
672 const uint8_t ack = POSIX_NETLINK_UDP_HELLO_ACK;
673
674 state->peer = src;
675 state->peer_len = src_len;
676 (void)sendto(state->fd, &ack, sizeof(ack), 0, (const struct sockaddr *)&src, src_len);
677 rte_log_write(RTE_LOG_LEVEL_INFO, "RTE", "RTE: UDP listen re-learned a restarted peer");
678 }
679 continue;
680 }
681 if ((n == (ssize_t)sizeof(uint8_t)) && state->listen && (((const uint8_t *)out_message)[0] == POSIX_NETLINK_UDP_HELLO))
682 {
683 /* HELLO from the known peer address (retransmit): acknowledge again. */
684 const uint8_t ack = POSIX_NETLINK_UDP_HELLO_ACK;
685
686 (void)sendto(state->fd, &ack, sizeof(ack), 0, (const struct sockaddr *)&src, src_len);
687 continue;
688 }
689 }
690 if (n < 0)
691 {
692 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
693 {
694 continue;
695 }
696 if (errno == ECONNREFUSED)
697 {
698 return RTE_STATUS_HARDWARE_FAULT;
699 }
700 return RTE_STATUS_INTERNAL_ERROR;
701 }
702 if ((size_t)n != buffer_size)
703 {
704 /* A lone HELLO/HELLO_ACK byte that outlived the handshake (a
705 * retransmit already in flight when the peer stopped
706 * resending, or open()'s own final ACK arriving late) is not
707 * a wire violation - silently drop it and keep waiting within
708 * the same deadline, matching a plain "nothing new yet" wait.
709 * message_size is never 1 byte for any real link in this
710 * example (smallest is ANSWER_WIRE_SIZE/M136_WIRE_SIZE at 8),
711 * so this can never mask a genuine 1-byte application frame. */
712 if ((n == (ssize_t)sizeof(uint8_t)) &&
713 ((((const uint8_t *)out_message)[0] == POSIX_NETLINK_UDP_HELLO) ||
714 (((const uint8_t *)out_message)[0] == POSIX_NETLINK_UDP_HELLO_ACK)))
715 {
716 continue;
717 }
718 /* Anything else of the wrong length: this link's message_size
719 * is fixed by config, so a datagram of any other length -
720 * truncated, oversized, or a stray 0-byte datagram - is a
721 * genuine wire protocol violation, not a value to silently
722 * accept partial-length. */
723 return RTE_STATUS_DATA_CORRUPTION;
724 }
725 return RTE_STATUS_OK;
726 }
727}
728
729static rte_status_t osadapter_close(rte_netlink_handle_t handle)
730{
731 posix_netlink_state_t *state = (posix_netlink_state_t *)(void *)handle;
732
733 if (state == NULL)
734 {
735 return RTE_STATUS_INVALID_PARAM;
736 }
737 (void)close(state->fd);
738 return RTE_STATUS_OK;
739}
740
741static const rte_osadapter_netlink_t s_posix_netlink_osadapter = { osadapter_open, backend_send, backend_receive,
742 osadapter_close };
743
744const rte_osadapter_netlink_t *rte_posix_osadapter_netlink(void)
745{
746 return &s_posix_netlink_osadapter;
747}
Real POSIX/Linux OSAdapter for every OAL service (ADR-018).
const rte_osadapter_netlink_t * rte_posix_osadapter_netlink(void)
The POSIX rte_netlink OSAdapter (TCP sockets, LISTEN/CONNECT).
static bool compute_deadline(rte_duration_ms_t timeout_ms, struct timespec *out_deadline)
static rte_status_t backend_receive(rte_ipc_handle_t handle, void *out_message, size_t buffer_size, rte_duration_ms_t timeout_ms)
Private helpers: millisecond waits for poll()'s int timeout argument.
rte_status_t posix_socket_apply_buffers(int fd, const rte_netlink_config_t *config)
Sets and confirms config->rcvbuf_bytes (SO_RCVBUF) and config->sndbuf_bytes (SO_SNDBUF) on a socket.
Private helper: applies the socket buffer sizes of a link config (OSA rules R5, R6).