RTE OSAdapter POSIX 0.1.0 build 41b3eb3
POSIX/Linux OAL OSAdapter for Platform_RTE
Loading...
Searching...
No Matches
rte_posix_osadapter_channel_service.c
Go to the documentation of this file.
1#define _POSIX_C_SOURCE 200809L
2
4
6
7#include <arpa/inet.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <netdb.h>
11#include <netinet/in.h>
12#include <poll.h>
13#include <stdio.h>
14#include <time.h>
15#include <sys/socket.h>
16#include <sys/types.h>
17#include <unistd.h>
18
19#include "rte/oal/memory/rte_mem_util.h"
22
23typedef struct
24{
25 int fd;
26 rte_netlink_config_t config;
27 struct sockaddr_in peer_addr;
28 socklen_t peer_addr_len;
29 bool has_peer_addr;
30 bool initialized;
31 uint64_t last_rx_ms; /* connect role: when a datagram last arrived (0 = none yet) */
32 uint64_t last_hello_ms; /* connect role: when a hello was last sent */
34
35typedef char rte_posix_channel_state_fits_[(sizeof(rte_posix_channel_state_t) <=
36 sizeof(rte_channel_service_storage_t)) ? 1 : -1];
37
38/* A connect-role end that has learned its peer sends no hello any more, so a listen-role peer that restarted (its
39 * socket is new and knows nobody) would stay silent towards it for good: the listener only learns a peer from a
40 * datagram it receives. After this long without any datagram the connect end says hello again; a healthy listener
41 * just acknowledges it. */
42#define RTE_POSIX_CHANNEL_REHELLO_MS 2000U
43
44static uint64_t channel_now_ms(void)
45{
46 struct timespec ts;
47
48 (void)clock_gettime(CLOCK_MONOTONIC, &ts);
49 return ((uint64_t)ts.tv_sec * 1000U) + ((uint64_t)ts.tv_nsec / 1000000U);
50}
51
52static rte_posix_channel_resolve_fn s_resolver;
53static void *s_resolver_context;
54
55static rte_posix_channel_state_t *channel_state(rte_channel_service_storage_t *storage)
56{
57 return (rte_posix_channel_state_t *)(void *)storage;
58}
59
60static rte_status_t set_nonblocking(int fd)
61{
62 int flags = fcntl(fd, F_GETFL, 0);
63 if (flags < 0)
64 {
65 return RTE_STATUS_INTERNAL_ERROR;
66 }
67 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
68 {
69 return RTE_STATUS_INTERNAL_ERROR;
70 }
71 return RTE_STATUS_OK;
72}
73
74static rte_status_t init_listen_socket(rte_posix_channel_state_t *state)
75{
76 int fd;
77 int reuse = 1;
78 struct sockaddr_in bind_addr;
79
80 if (state->fd >= 0)
81 {
82 return RTE_STATUS_OK;
83 }
84
85 fd = socket(AF_INET, SOCK_DGRAM, 0);
86 if (fd >= 0)
87 {
88 /* Not inherited across execv(): the in-process REBOOT re-execs this binary, and an inherited listening socket
89 * kept receiving datagrams for its port that nobody read (Docker: sockets piled up with every reboot). */
90 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
91 }
92 if (fd < 0)
93 {
94 return RTE_STATUS_INTERNAL_ERROR;
95 }
96 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
97#ifdef SO_REUSEPORT
98 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
99#endif
100
101 rte_mem_set(&bind_addr, 0, sizeof(bind_addr));
102 bind_addr.sin_family = AF_INET;
103 bind_addr.sin_port = htons(state->config.port);
104 if (state->config.host == NULL)
105 {
106 bind_addr.sin_addr.s_addr = INADDR_ANY;
107 }
108 else if (inet_pton(AF_INET, state->config.host, &bind_addr.sin_addr) != 1)
109 {
110 (void)close(fd);
111 return RTE_STATUS_INVALID_PARAM;
112 }
113
114 if (bind(fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0)
115 {
116 (void)close(fd);
117 return RTE_STATUS_INTERNAL_ERROR;
118 }
119 /* REQ-OAL-NETLINK-015: configured buffer sizes on every (re)init; both 0 -> no call at all. */
120 {
121 const rte_status_t buf_status = posix_socket_apply_buffers(fd, &state->config);
122
123 if (buf_status != RTE_STATUS_OK)
124 {
125 (void)close(fd);
126 return buf_status;
127 }
128 }
129 if (set_nonblocking(fd) != RTE_STATUS_OK)
130 {
131 (void)close(fd);
132 return RTE_STATUS_INTERNAL_ERROR;
133 }
134 state->fd = fd;
135 return RTE_STATUS_OK;
136}
137
138static rte_status_t init_connect_socket(rte_posix_channel_state_t *state)
139{
140 struct addrinfo hints;
141 struct addrinfo *resolved = NULL;
142 char port_str[8];
143 int fd;
144
145 if (state->fd >= 0)
146 {
147 return RTE_STATUS_OK;
148 }
149
150 (void)snprintf(port_str, sizeof(port_str), "%u", (unsigned int)state->config.port);
151 rte_mem_set(&hints, 0, sizeof(hints));
152 hints.ai_family = AF_INET;
153 hints.ai_socktype = SOCK_DGRAM;
154
155 if (getaddrinfo(state->config.host, port_str, &hints, &resolved) != 0)
156 {
157 return RTE_STATUS_INVALID_PARAM;
158 }
159
160 fd = socket(resolved->ai_family, resolved->ai_socktype, resolved->ai_protocol);
161 if (fd >= 0)
162 {
163 /* Not inherited across execv(): the in-process REBOOT re-execs this binary, and an inherited listening socket
164 * kept receiving datagrams for its port that nobody read (Docker: sockets piled up with every reboot). */
165 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
166 }
167 if (fd < 0)
168 {
169 perror("init_connect_socket: socket");
170 freeaddrinfo(resolved);
171 return RTE_STATUS_INTERNAL_ERROR;
172 }
173 /* REQ-OAL-NETLINK-015: configured buffer sizes on every (re)init; both 0 -> no call at all. */
174 {
175 const rte_status_t buf_status = posix_socket_apply_buffers(fd, &state->config);
176
177 if (buf_status != RTE_STATUS_OK)
178 {
179 (void)close(fd);
180 freeaddrinfo(resolved);
181 return buf_status;
182 }
183 }
184 if (set_nonblocking(fd) != RTE_STATUS_OK)
185 {
186 perror("init_connect_socket: set_nonblocking");
187 (void)close(fd);
188 freeaddrinfo(resolved);
189 return RTE_STATUS_INTERNAL_ERROR;
190 }
191
192 if (connect(fd, resolved->ai_addr, resolved->ai_addrlen) != 0)
193 {
194 perror("init_connect_socket: connect");
195 (void)close(fd);
196 freeaddrinfo(resolved);
197 return RTE_STATUS_INTERNAL_ERROR;
198 }
199 freeaddrinfo(resolved);
200 state->fd = fd;
201 {
202 uint8_t hello = 0xA5U;
203 (void)send(state->fd, &hello, 1, 0);
204 }
205 return RTE_STATUS_OK;
206}
207
208static rte_status_t osadapter_setup(rte_channel_service_storage_t *storage, const char *channel_name)
209{
211 rte_status_t status;
212
213 if ((storage == NULL) || (channel_name == NULL) || (s_resolver == NULL))
214 {
215 return RTE_STATUS_INVALID_PARAM;
216 }
217 state = channel_state(storage);
218 rte_mem_set(state, 0, sizeof(*state));
219 state->fd = -1;
220 status = s_resolver(channel_name, &state->config, s_resolver_context);
221 if (status != RTE_STATUS_OK)
222 {
223 return status;
224 }
225 state->initialized = true;
226 if (state->config.role == RTE_NETLINK_ROLE_LISTEN)
227 {
228 return init_listen_socket(state);
229 }
230 return init_connect_socket(state);
231}
232
233/* Shared by osadapter_read and osadapter_read_ex: recvfrom()/recv() already return the true
234 * datagram length in n before either wrapper discards it - out_actual_size, when non-NULL, is
235 * set to that value on RTE_STATUS_OK. Behaviour is otherwise identical to the old single
236 * osadapter_read function this replaces (same hello/ack handshake handling, same status
237 * mapping), verified by construction rather than duplicated by hand. */
238static rte_status_t do_read(rte_channel_service_storage_t *storage, void *data, size_t data_size,
239 rte_duration_ms_t timeout_ms, size_t *out_actual_size)
240{
242 ssize_t n;
243
244 if ((storage == NULL) || (data == NULL) || (data_size == 0U))
245 {
246 return RTE_STATUS_INVALID_PARAM;
247 }
248 state = channel_state(storage);
249 if (!state->initialized)
250 {
251 return RTE_STATUS_NOT_INITIALIZED;
252 }
253 if (state->fd < 0)
254 {
255 if (state->config.role == RTE_NETLINK_ROLE_LISTEN)
256 {
257 if (init_listen_socket(state) != RTE_STATUS_OK)
258 {
259 return RTE_STATUS_TIMEOUT;
260 }
261 }
262 else
263 {
264 if (init_connect_socket(state) != RTE_STATUS_OK)
265 {
266 return RTE_STATUS_TIMEOUT;
267 }
268 }
269 }
270
271 if (timeout_ms > 0U)
272 {
273 struct pollfd pfd;
274 int rc;
275
276 pfd.fd = state->fd;
277 pfd.events = POLLIN;
278 pfd.revents = 0;
279 rc = poll(&pfd, 1, posix_poll_timeout_ms(timeout_ms));
280 if (rc == 0)
281 {
282 return RTE_STATUS_TIMEOUT;
283 }
284 if (rc < 0)
285 {
286 if (errno == EINTR)
287 {
288 return RTE_STATUS_TIMEOUT;
289 }
290 return RTE_STATUS_INTERNAL_ERROR;
291 }
292 }
293
294 if (state->config.role == RTE_NETLINK_ROLE_LISTEN)
295 {
296 while (1)
297 {
298 struct sockaddr_in src_addr;
299 socklen_t addr_len = (socklen_t)sizeof(src_addr);
300 rte_mem_set(&src_addr, 0, sizeof(src_addr));
301
302 n = recvfrom(state->fd, data, data_size, 0, (struct sockaddr *)&src_addr, &addr_len);
303 if (n > 0)
304 {
305 state->peer_addr = src_addr;
306 state->peer_addr_len = addr_len;
307 state->has_peer_addr = true;
308
309 if ((n == 1) && (((const uint8_t *)data)[0] == 0xA5U))
310 {
311 uint8_t ack = 0x5AU;
312 (void)sendto(state->fd, &ack, 1, 0, (const struct sockaddr *)&src_addr, addr_len);
313 continue;
314 }
315 }
316 break;
317 }
318 }
319 else
320 {
321 while (1)
322 {
323 const uint64_t now_ms = channel_now_ms();
324
325 if (!state->has_peer_addr)
326 {
327 uint8_t hello = 0xA5U;
328 (void)send(state->fd, &hello, 1, 0);
329 state->last_hello_ms = now_ms;
330 }
331 else
332 {
333 if (state->last_rx_ms == 0U)
334 {
335 state->last_rx_ms = now_ms;
336 }
337 if (((now_ms - state->last_rx_ms) >= RTE_POSIX_CHANNEL_REHELLO_MS) &&
338 ((now_ms - state->last_hello_ms) >= RTE_POSIX_CHANNEL_REHELLO_MS))
339 {
340 uint8_t hello = 0xA5U;
341 (void)send(state->fd, &hello, 1, 0);
342 state->last_hello_ms = now_ms;
343 }
344 }
345 n = recv(state->fd, data, data_size, 0);
346 if (n > 0)
347 {
348 state->last_rx_ms = now_ms;
349 if ((n == 1) && (((const uint8_t *)data)[0] == 0x5AU))
350 {
351 state->has_peer_addr = true;
352 continue;
353 }
354 if ((n == 1) && (((const uint8_t *)data)[0] == 0xA5U))
355 {
356 uint8_t ack = 0x5AU;
357 (void)send(state->fd, &ack, 1, 0);
358 state->has_peer_addr = true;
359 continue;
360 }
361 }
362 break;
363 }
364 }
365
366 if (n < 0)
367 {
368 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
369 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EPIPE))
370 {
371 return RTE_STATUS_TIMEOUT;
372 }
373 return RTE_STATUS_INTERNAL_ERROR;
374 }
375 if (n == 0)
376 {
377 return RTE_STATUS_TIMEOUT;
378 }
379 if (out_actual_size != NULL)
380 {
381 *out_actual_size = (size_t)n;
382 }
383 return RTE_STATUS_OK;
384}
385
386static rte_status_t osadapter_read(rte_channel_service_storage_t *storage, void *data, size_t data_size,
387 rte_duration_ms_t timeout_ms)
388{
389 return do_read(storage, data, data_size, timeout_ms, NULL);
390}
391
392static rte_status_t osadapter_read_ex(rte_channel_service_storage_t *storage, void *data, size_t data_size,
393 rte_duration_ms_t timeout_ms, size_t *out_actual_size)
394{
395 if (out_actual_size == NULL)
396 {
397 return RTE_STATUS_INVALID_PARAM;
398 }
399 return do_read(storage, data, data_size, timeout_ms, out_actual_size);
400}
401
402static rte_status_t backend_send(rte_channel_service_storage_t *storage, const void *data, size_t data_size,
403 rte_duration_ms_t timeout_ms)
404{
406 ssize_t n;
407
408 if ((storage == NULL) || (data == NULL) || (data_size == 0U))
409 {
410 return RTE_STATUS_INVALID_PARAM;
411 }
412 state = channel_state(storage);
413 if (!state->initialized)
414 {
415 return RTE_STATUS_NOT_INITIALIZED;
416 }
417 if (state->fd < 0)
418 {
419 if (state->config.role == RTE_NETLINK_ROLE_LISTEN)
420 {
421 if (init_listen_socket(state) != RTE_STATUS_OK)
422 {
423 return RTE_STATUS_TIMEOUT;
424 }
425 }
426 else
427 {
428 if (init_connect_socket(state) != RTE_STATUS_OK)
429 {
430 return RTE_STATUS_TIMEOUT;
431 }
432 }
433 }
434
435 if (timeout_ms > 0U)
436 {
437 struct pollfd pfd;
438 int rc;
439
440 pfd.fd = state->fd;
441 pfd.events = POLLOUT;
442 pfd.revents = 0;
443 rc = poll(&pfd, 1, posix_poll_timeout_ms(timeout_ms));
444 if (rc == 0)
445 {
446 return RTE_STATUS_TIMEOUT;
447 }
448 if (rc < 0)
449 {
450 if (errno == EINTR)
451 {
452 return RTE_STATUS_TIMEOUT;
453 }
454 return RTE_STATUS_INTERNAL_ERROR;
455 }
456 }
457
458 if (state->config.role == RTE_NETLINK_ROLE_LISTEN)
459 {
460 if (!state->has_peer_addr)
461 {
462 /* REQ-CHANSVC-001 (Platform_RTE, OSA rule R5): no peer has spoken to this listen socket yet, so there is
463 * no address to send to. Nothing is sent, and the caller is told so instead of getting OK. */
464 return RTE_STATUS_NOT_CONNECTED;
465 }
466 n = sendto(state->fd, data, data_size, 0, (const struct sockaddr *)&state->peer_addr, state->peer_addr_len);
467 }
468 else
469 {
470 n = send(state->fd, data, data_size, 0);
471 }
472
473 if (n < 0)
474 {
475 if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ECONNREFUSED) ||
476 (errno == ENOTCONN) || (errno == EHOSTUNREACH) || (errno == EADDRNOTAVAIL) || (errno == EPIPE))
477 {
478 return RTE_STATUS_TIMEOUT;
479 }
480 return RTE_STATUS_INTERNAL_ERROR;
481 }
482 return RTE_STATUS_OK;
483}
484
485static rte_status_t osadapter_close(rte_channel_service_storage_t *storage)
486{
488
489 if (storage == NULL)
490 {
491 return RTE_STATUS_OK;
492 }
493 state = channel_state(storage);
494 if (!state->initialized)
495 {
496 return RTE_STATUS_OK;
497 }
498 state->initialized = false;
499 if (state->fd >= 0)
500 {
501 (void)close(state->fd);
502 state->fd = -1;
503 }
504 state->has_peer_addr = false;
505 return RTE_STATUS_OK;
506}
507
508static const rte_osadapter_channel_service_t s_osadapter = {
509 osadapter_setup,
510 osadapter_read,
511 backend_send,
512 osadapter_close,
513 osadapter_read_ex
514};
515
517{
518 if (resolver == NULL)
519 {
520 return RTE_STATUS_INVALID_PARAM;
521 }
522 s_resolver = resolver;
523 s_resolver_context = context;
524 return rte_osadapter_channel_service_register(&s_osadapter);
525}
526
527const rte_osadapter_channel_service_t *rte_posix_osadapter_channel_service(void)
528{
529 return &s_osadapter;
530}
POSIX named-channel adapter.
rte_status_t rte_posix_osadapter_channel_service_register_resolver(rte_posix_channel_resolve_fn resolver, void *context)
Registers the channel-name resolver and the POSIX channel service OSAdapter (ADR-005 seam).
const rte_osadapter_channel_service_t * rte_posix_osadapter_channel_service(void)
The POSIX channel service OSAdapter vtable (UDP; setup binds or dials and returns,...
rte_status_t(*) rte_posix_channel_resolve_fn(const char *channel_name, rte_netlink_config_t *out_config, void *context)
Resolves a channel name to its link configuration (integrator-supplied).
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).