RBC_Test_CommServer 0.1.0 build fe9835b
Non-vital customer-network access point in front of the RBC's role-C gateways
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1
14#include <arpa/inet.h>
15#include <errno.h>
16#include <netinet/in.h>
17#include <poll.h>
18#include <signal.h>
19#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/socket.h>
24#include <unistd.h>
25
26#include "safecomm/config/config.h"
27#include "safecomm/ale/ale.h"
28
29#include "rte/oal/netlink/rte_netlink.h"
30#include "rte/posix_osadapter/rte_posix_osadapter.h"
31
32#include "cs_config.h"
33
35#define CS_DRAIN_MAX 256U
36
37static volatile sig_atomic_t g_stop = 0;
38static bool g_trace = false; /* COMMSERVER_TRACE in env */
39
40static void on_signal(int sig)
41{
42 (void)sig;
43 g_stop = 1;
44}
45
47static void trace_frame(const char *dir, const uint8_t *f, size_t n)
48{
49 if (!g_trace || (n < 4U))
50 {
51 return;
52 }
53 (void)fprintf(stderr, "commserver: TRACE %-12s kind=%3u train_id=%u nid_msg=%u\n",
54 dir, (unsigned int)f[0], (unsigned int)f[1], (unsigned int)f[2]);
55}
56
57static int tcp_listen(const char *host, uint16_t port)
58{
59 struct sockaddr_in addr;
60 int fd;
61 int one = 1;
62
63 fd = socket(AF_INET, SOCK_STREAM, 0);
64 if (fd < 0)
65 {
66 return -1;
67 }
68 (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
69
70 (void)memset(&addr, 0, sizeof(addr));
71 addr.sin_family = AF_INET;
72 addr.sin_port = htons(port);
73 if (inet_pton(AF_INET, host, &addr.sin_addr) != 1)
74 {
75 addr.sin_addr.s_addr = htonl(INADDR_ANY);
76 }
77 if ((bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) != 0) || (listen(fd, 1) != 0))
78 {
79 (void)close(fd);
80 return -1;
81 }
82 return fd;
83}
84
85static rte_status_t netlink_connect(const char *host, uint16_t port, uint16_t msg_size,
86 uint32_t timeout_ms, rte_netlink_storage_t *storage,
87 rte_netlink_handle_t *out)
88{
89 rte_netlink_config_t nc;
90
91 (void)memset(&nc, 0, sizeof(nc));
92 nc.role = RTE_NETLINK_ROLE_CONNECT;
93 nc.host = host;
94 nc.port = port;
95 nc.message_size = (size_t)msg_size;
96 nc.connect_timeout_ms = (rte_duration_ms_t)timeout_ms;
97 return rte_netlink_open(storage, &nc, out);
98}
99
101static void serve_client(int cfd, const cs_cfg_t *cfg)
102{
103 rte_netlink_storage_t west_storage;
104 rte_netlink_storage_t east_storage;
105 rte_netlink_handle_t west = NULL;
106 rte_netlink_handle_t east = NULL;
107 uint8_t up[CS_MSG_MAX];
108 uint8_t dn[CS_MSG_MAX];
109 size_t up_fill = 0U;
110 const size_t msz = (size_t)cfg->message_size;
111
112 if (netlink_connect(cfg->west_host, cfg->west_port, cfg->message_size,
113 cfg->connect_timeout_ms, &west_storage, &west) != RTE_STATUS_OK)
114 {
115 (void)fprintf(stderr, "commserver: WEST link (%s:%u) not established\n",
116 cfg->west_host, cfg->west_port);
117 west = NULL;
118 }
119 if (netlink_connect(cfg->east_host, cfg->east_port, cfg->message_size,
120 cfg->connect_timeout_ms, &east_storage, &east) != RTE_STATUS_OK)
121 {
122 (void)fprintf(stderr, "commserver: EAST link (%s:%u) not established\n",
123 cfg->east_host, cfg->east_port);
124 east = NULL;
125 }
126 if ((west == NULL) && (east == NULL))
127 {
128 (void)fprintf(stderr, "commserver: no RBC link — dropping train\n");
129 return;
130 }
131 (void)fprintf(stderr, "commserver: train connected; relaying (west=%s east=%s)\n",
132 (west != NULL) ? "up" : "down", (east != NULL) ? "up" : "down");
133
134 while (g_stop == 0)
135 {
136 struct pollfd pfd;
137 int pr;
138 bool closed = false;
139 uint8_t site;
140
141 pfd.fd = cfd;
142 pfd.events = POLLIN;
143 /* Short poll timeout: the loop must come back around quickly to
144 * drain the RBC->train direction. A long wait here (an earlier
145 * bug used poll.idle_ms) let RBC reply datagrams pile up in the
146 * UDP socket buffer and get dropped, so MA / M24 / indication
147 * traffic never reached the train - every MA-dependent scenario
148 * test timed out. */
149 pr = poll(&pfd, 1U, 1);
150 if (pr < 0)
151 {
152 if (errno == EINTR)
153 {
154 continue;
155 }
156 break;
157 }
158
159 /* train -> RBC: drain EVERY whole frame available this pass, not
160 * one partial read. recv(MSG_DONTWAIT) keeps write(cfd) blocking. */
161 for (;;)
162 {
163 ssize_t n = recv(cfd, &up[up_fill], msz - up_fill, MSG_DONTWAIT);
164
165 if (n == 0)
166 {
167 closed = true;
168 break;
169 }
170 if (n < 0)
171 {
172 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
173 {
174 break;
175 }
176 if (errno == EINTR)
177 {
178 continue;
179 }
180 closed = true;
181 break;
182 }
183 up_fill += (size_t)n;
184 if (up_fill == msz)
185 {
186 if (west != NULL)
187 {
188 (void)rte_netlink_send(west, up, msz, 200U);
189 trace_frame("train->WEST", up, msz);
190 }
191 if (east != NULL)
192 {
193 (void)rte_netlink_send(east, up, msz, 200U);
194 trace_frame("train->EAST", up, msz);
195 }
196 up_fill = 0U;
197 }
198 }
199 if (closed)
200 {
201 break;
202 }
203
204 /* RBC -> train: drain each site until it has nothing left (bounded
205 * so a flooding peer cannot starve the train read above). */
206 for (site = 0U; (site < 2U) && !closed; site++)
207 {
208 rte_netlink_handle_t h = (site == 0U) ? west : east;
209 uint32_t guard = 0U;
210
211 while ((h != NULL) && (guard < CS_DRAIN_MAX))
212 {
213 guard++;
214 /* 2 ms, not 0: a 0-timeout receive returns before a
215 * just-sent localhost reply datagram has landed, so the
216 * drain would keep missing it. With the drain loop the
217 * 2 ms is paid once per site per pass (when the queue is
218 * finally empty), not per datagram. */
219 if (rte_netlink_receive(h, dn, msz, 2U) != RTE_STATUS_OK)
220 {
221 break;
222 }
223 trace_frame((site == 0U) ? "WEST->train" : "EAST->train", dn, msz);
224 if (write(cfd, dn, msz) != (ssize_t)msz)
225 {
226 closed = true;
227 break;
228 }
229 }
230 }
231 if (closed)
232 {
233 break;
234 }
235 }
236
237 if (west != NULL)
238 {
239 (void)rte_netlink_close(west);
240 }
241 if (east != NULL)
242 {
243 (void)rte_netlink_close(east);
244 }
245 (void)fprintf(stderr, "commserver: train disconnected\n");
246}
247
248int main(int argc, char **argv)
249{
250 cs_cfg_t cfg;
251 int lfd;
252
253 if (argc < 2)
254 {
255 (void)fprintf(stderr, "usage: %s <commserver.conf>\n", argv[0]);
256 return 2;
257 }
258 if (load_cfg(argv[1], &cfg) != 0)
259 {
260 return 1;
261 }
262
263 (void)signal(SIGINT, on_signal);
264 (void)signal(SIGTERM, on_signal);
265 (void)signal(SIGPIPE, SIG_IGN);
266
267 g_trace = (getenv("COMMSERVER_TRACE") != NULL);
268
269 if (rte_posix_osadapter_register_all() != RTE_STATUS_OK)
270 {
271 (void)fprintf(stderr, "commserver: rte_posix_osadapter_register_all failed\n");
272 return 1;
273 }
274
275 lfd = tcp_listen(cfg.listen_host, cfg.listen_port);
276 if (lfd < 0)
277 {
278 (void)fprintf(stderr, "commserver: cannot listen on %s:%u\n", cfg.listen_host, cfg.listen_port);
279 return 1;
280 }
281 (void)fprintf(stderr, "commserver: listening on %s:%u — RBC west %s:%u / east %s:%u — msg %u\n",
282 cfg.listen_host, cfg.listen_port, cfg.west_host, cfg.west_port,
283 cfg.east_host, cfg.east_port, cfg.message_size);
284
285 while (g_stop == 0)
286 {
287 int cfd = accept(lfd, NULL, NULL);
288
289 if (cfd < 0)
290 {
291 if (errno == EINTR)
292 {
293 continue;
294 }
295 break;
296 }
297 serve_client(cfd, &cfg);
298 (void)close(cfd);
299 }
300
301 (void)close(lfd);
302 (void)fprintf(stderr, "commserver: stopped\n");
303 return 0;
304}
int load_cfg(const char *path, cs_cfg_t *cfg)
Loads path into cfg, defaults first then overridden by whatever the file actually sets (out-of-range ...
Definition cs_config.c:42
CommServer's own config parsing (cs_cfg_t + load_cfg()), extracted from main.c (2026-09-24) purely fo...
#define CS_DRAIN_MAX
Definition main.c:35