1 /*
2 * Copyright (C) 2021-2025 Red Hat, Inc. All rights reserved.
3 *
4 * Authors: Christine Caulfield <ccaulfie@redhat.com>
5 *
6 * This software licensed under GPL-2.0+
7 */
8
9 #include "config.h"
10
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <inttypes.h>
17 #include <pthread.h>
18 #include <poll.h>
19
20 #include "libknet.h"
21
22 #include "internals.h"
23 #include "netutils.h"
24 #include "test-common.h"
25
26
27 /*
28 * Keep track of how many messages got through:
29 * clean + 3xACLs + QUIT
30 */
31 #define CORRECT_NUM_MSGS 5
32 static int msgs_recvd = 0;
33
34 #undef TESTNODES
35 #define TESTNODES 2
36
37 static pthread_mutex_t recv_mutex = PTHREAD_MUTEX_INITIALIZER;
38 static int quit_recv_thread = 0;
39
40 static int reply_pipe[2];
41
42 /* Our local version of FOE that also tidies up the threads */
43 #define FAIL_ON_ERR_THR(fn) \
44 printf("FOE: %s\n", #fn); \
45 if ((res = fn) != 0) { \
46 int savederrno = errno; \
47 pthread_mutex_lock(&recv_mutex); \
48 quit_recv_thread = 1; \
49 pthread_mutex_unlock(&recv_mutex); \
50 if (recv_thread) { \
51 pthread_join(recv_thread, (void**)&thread_err); \
52 } \
53 knet_handle_stop_everything(knet_h, TESTNODES); \
54 stop_logthread(); \
55 flush_logs(logfds[0], stdout); \
56 close_logpipes(logfds); \
57 close(reply_pipe[0]); \
58 close(reply_pipe[1]); \
59 if (res == -2) { \
60 exit(SKIP); \
61 } else { \
62 printf("*** FAIL on line %d %s failed: %s\n", __LINE__ , #fn, strerror(savederrno)); \
63 exit(FAIL); \
64 } \
65 }
66
67
68 static int knet_send_str(knet_handle_t knet_h, char *str)
69 {
|
(1) Event lock: |
"knet_send_sync" locks "knet_h->global_rwlock". [details] |
|
(2) Event missing_unlock: |
Returning without unlocking "knet_h->global_rwlock". |
70 return knet_send_sync(knet_h, str, strlen(str)+1, 0);
71 }
72
73 /*
74 * lo0 is filled in with the local address on return.
75 * lo1 is expected to be provided - it's the actual remote address to connect to.
76 */
77 int dyn_knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
78 uint8_t transport, uint64_t flags, int family, int dynamic,
79 struct sockaddr_storage *lo0, struct sockaddr_storage *lo1)
80 {
81 int err = 0, savederrno = 0;
82 uint32_t port;
83 char portstr[32];
84
85 for (port = 1025; port < 65536; port++) {
86 sprintf(portstr, "%u", port);
87 memset(lo0, 0, sizeof(struct sockaddr_storage));
88 if (family == AF_INET6) {
89 err = knet_strtoaddr("::1", portstr, lo0, sizeof(struct sockaddr_storage));
90 } else {
91 err = knet_strtoaddr("127.0.0.1", portstr, lo0, sizeof(struct sockaddr_storage));
92 }
93 if (err < 0) {
94 printf("Unable to convert loopback to sockaddr: %s\n", strerror(errno));
95 goto out;
96 }
97 errno = 0;
98 if (dynamic) {
99 err = knet_link_set_config(knet_h, host_id, link_id, transport, lo0, NULL, flags);
100 } else {
101 err = knet_link_set_config(knet_h, host_id, link_id, transport, lo0, lo1, flags);
102 }
103 savederrno = errno;
104 if ((err < 0) && (savederrno != EADDRINUSE)) {
105 if (savederrno == EPROTONOSUPPORT && transport == KNET_TRANSPORT_SCTP) {
106 return -2;
107 } else {
108 printf("Unable to configure link: %s\n", strerror(savederrno));
109 goto out;
110 }
111 }
112 if (!err) {
113 printf("Using port %u\n", port);
114 goto out;
115 }
116 }
117
118 if (err) {
119 printf("No more ports available\n");
120 }
121 out:
122 errno = savederrno;
123 return err;
124 }
125
126 static void *recv_messages(void *handle)
127 {
128 knet_handle_t knet_h = (knet_handle_t)handle;
129 char buf[4096];
130 ssize_t len;
131 static int err = 0;
132 int savederrno = 0, quit = 0;
133
134 while ((len = knet_recv(knet_h, buf, sizeof(buf), 0)) && (!quit)) {
135 savederrno = errno;
136 pthread_mutex_lock(&recv_mutex);
137 quit = quit_recv_thread;
138 pthread_mutex_unlock(&recv_mutex);
139 if (quit) {
140 printf(" *** recv thread was requested to exit via FOE\n");
141 err = 1;
142 return &err;
143 }
144 if (len > 0) {
145 int res;
146
147 printf("recv: (%ld) %s\n", (long)len, buf);
148 msgs_recvd++;
149 if (strcmp("QUIT", buf) == 0) {
150 break;
151 }
152 if (buf[0] == '0') { /* We should not have received this! */
153 printf(" *** FAIL received packet that should have been blocked\n");
154 err = 1;
155 return &err;
156 }
157 /* Tell the main thread we have received something */
158 res = write(reply_pipe[1], ".", 1);
159 if (res != 1) {
160 printf(" *** FAIL to send response back to main thread\n");
161 err = 1;
162 return &err;
163 }
164 }
165 usleep(1000);
166 if (len < 0 && savederrno != EAGAIN) {
167 break;
168 }
169 }
170 printf("-- recv thread finished: %zd %d %s\n", len, errno, strerror(savederrno));
171 return &err;
172 }
173
174 static void notify_fn(void *private_data,
175 int datafd,
176 int8_t channel,
177 uint8_t tx_rx,
178 int error,
179 int errorno)
180 {
181 printf("NOTIFY fn called\n");
182 }
183
184 /* A VERY basic filter because all data traffic is going to one place */
185 static int dhost_filter(void *pvt_data,
186 const unsigned char *outdata,
187 ssize_t outdata_len,
188 uint8_t tx_rx,
189 knet_node_id_t this_host_id,
190 knet_node_id_t src_host_id,
191 int8_t *dst_channel,
192 knet_node_id_t *dst_host_ids,
193 size_t *dst_host_ids_entries)
194 {
195 dst_host_ids[0] = 1;
196 *dst_host_ids_entries = 1;
197 return 0;
198 }
199
200 /* This used to be a pthread condition variable, but
201 there was a race where it could be triggered before
202 the main thread was waiting for it.
203 Go old-fashioned.
204 */
205 static int wait_for_reply(int seconds)
206 {
207 int res;
208 struct pollfd pfds;
209 char tmpbuf[32];
210
211 pfds.fd = reply_pipe[0];
212 pfds.events = POLLIN | POLLERR | POLLHUP;
213 pfds.revents = 0;
214
215 res = poll(&pfds, 1, seconds*1000);
216 if (res == 1) {
217 if (pfds.revents & POLLIN) {
218 res = read(reply_pipe[0], tmpbuf, sizeof(tmpbuf));
219 if (res > 0) {
220 return 0;
221 }
222 } else {
223 printf("Error on pipe poll revent = 0x%x\n", pfds.revents);
224 errno = EIO;
225 }
226 }
227 if (res == 0) {
228 errno = ETIMEDOUT;
229 return -1;
230 }
231
232 return -1;
233 }
234
235 static void test(int transport)
236 {
237 knet_handle_t knet_h[TESTNODES+1];
238 int logfds[2];
239 struct sockaddr_storage lo0, lo1;
240 struct sockaddr_storage ss1, ss2;
241 int res;
242 pthread_t recv_thread = 0;
243 int *thread_err;
244 int datafd;
245 int8_t channel;
246 int seconds = 90; // dynamic tests take longer than normal tests
247
248 if (is_memcheck() || is_helgrind()) {
249 printf("Test suite is running under valgrind, adjusting wait_for_host timeout\n");
250 seconds = seconds * 16;
251 }
252
253 memset(knet_h, 0, sizeof(knet_h));
254 memset(reply_pipe, 0, sizeof(reply_pipe));
255 memset(logfds, 0, sizeof(logfds));
256
257 FAIL_ON_ERR_THR(pipe(reply_pipe));
258
259 // Initial setup gubbins
260 msgs_recvd = 0;
261 setup_logpipes(logfds);
262 start_logthread(logfds[1], stdout);
263 knet_handle_start_nodes(knet_h, TESTNODES, logfds, KNET_LOG_DEBUG);
264
265 FAIL_ON_ERR_THR(knet_host_add(knet_h[2], 1));
266 FAIL_ON_ERR_THR(knet_host_add(knet_h[1], 2));
267
268 FAIL_ON_ERR_THR(knet_handle_enable_filter(knet_h[2], NULL, dhost_filter));
269
270 // Create the dynamic (receiving) link
271 FAIL_ON_ERR_THR(dyn_knet_link_set_config(knet_h[1], 2, 0, transport, 0, AF_INET, 1, &lo0, NULL));
272
273 // Connect to the dynamic link
274 FAIL_ON_ERR_THR(dyn_knet_link_set_config(knet_h[2], 1, 0, transport, 0, AF_INET, 0, &lo1, &lo0));
275
276 // All the rest of the setup gubbins
277 FAIL_ON_ERR_THR(knet_handle_enable_sock_notify(knet_h[1], 0, ¬ify_fn));
278 FAIL_ON_ERR_THR(knet_handle_enable_sock_notify(knet_h[2], 0, ¬ify_fn));
279
280 channel = datafd = 0;
281 FAIL_ON_ERR_THR(knet_handle_add_datafd(knet_h[1], &datafd, &channel));
282 channel = datafd = 0;
283 FAIL_ON_ERR_THR(knet_handle_add_datafd(knet_h[2], &datafd, &channel));
284
285 FAIL_ON_ERR_THR(knet_link_set_enable(knet_h[1], 2, 0, 1));
286 FAIL_ON_ERR_THR(knet_link_set_enable(knet_h[2], 1, 0, 1));
287
288 FAIL_ON_ERR_THR(knet_handle_setfwd(knet_h[1], 1));
289 FAIL_ON_ERR_THR(knet_handle_setfwd(knet_h[2], 1));
290
291 // Start receive thread
292 FAIL_ON_ERR_THR(pthread_create(&recv_thread, NULL, recv_messages, (void *)knet_h[1]));
293
294 // Let everything settle down
295 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 1, seconds, logfds[0], stdout));
296 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 1, seconds, logfds[0], stdout));
297
298 /*
299 * TESTING STARTS HERE
300 * strings starting '1' should reach the receiving thread
301 * strings starting '0' should not
302 */
303
304 // No ACL
305 printf("Testing No ACL - this should get through\n");
306 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "1No ACL - this should get through"));
307 FAIL_ON_ERR_THR(wait_for_reply(seconds))
308
309 // Block traffic from this address.
310 memset(&ss1, 0, sizeof(ss1));
311 memset(&ss2, 0, sizeof(ss1));
312 knet_strtoaddr("127.0.0.1","0", &ss1, sizeof(ss1));
313 FAIL_ON_ERR_THR(knet_link_add_acl(knet_h[1], 2, 0, &ss1, NULL, CHECK_TYPE_ADDRESS, CHECK_REJECT));
314 // Accept ACL for when we remove them
315 FAIL_ON_ERR_THR(knet_link_add_acl(knet_h[1], 2, 0, &ss1, NULL, CHECK_TYPE_ADDRESS, CHECK_ACCEPT));
316
317 // This needs to go after the first ACLs are added
318 FAIL_ON_ERR_THR(knet_handle_enable_access_lists(knet_h[1], 1));
319
320 printf("Testing Address blocked - this should NOT get through\n");
321 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "0Address blocked - this should NOT get through"));
322
323 // Unblock and check again
324 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 0, seconds, logfds[0], stdout));
325 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 0, seconds, logfds[0], stdout));
326 FAIL_ON_ERR_THR(knet_link_rm_acl(knet_h[1], 2, 0, &ss1, NULL, CHECK_TYPE_ADDRESS, CHECK_REJECT));
327 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 1, seconds, logfds[0], stdout));
328 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 1, seconds, logfds[0], stdout));
329
330 printf("Testing Address unblocked - this should get through\n");
331 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "1Address unblocked - this should get through"));
332 FAIL_ON_ERR_THR(wait_for_reply(seconds));
333
334 // Block traffic using a netmask
335 knet_strtoaddr("127.0.0.1","0", &ss1, sizeof(ss1));
336 knet_strtoaddr("255.0.0.1","0", &ss2, sizeof(ss2));
337 FAIL_ON_ERR_THR(knet_link_insert_acl(knet_h[1], 2, 0, 0, &ss1, &ss2, CHECK_TYPE_MASK, CHECK_REJECT));
338
339 printf("Testing Netmask blocked - this should NOT get through\n");
340 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "0Netmask blocked - this should NOT get through"));
341
342 // Unblock and check again
343 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 0, seconds, logfds[0], stdout));
344 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 0, seconds, logfds[0], stdout));
345 FAIL_ON_ERR_THR(knet_link_rm_acl(knet_h[1], 2, 0, &ss1, &ss2, CHECK_TYPE_MASK, CHECK_REJECT));
346 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 1, seconds, logfds[0], stdout));
347 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 1, seconds, logfds[0], stdout));
348
349 printf("Testing Netmask unblocked - this should get through\n");
350 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "1Netmask unblocked - this should get through"));
351 FAIL_ON_ERR_THR(wait_for_reply(seconds));
352
353 // Block traffic from a range
354 knet_strtoaddr("127.0.0.0", "0", &ss1, sizeof(ss1));
355 knet_strtoaddr("127.0.0.9", "0", &ss2, sizeof(ss2));
356 FAIL_ON_ERR_THR(knet_link_insert_acl(knet_h[1], 2, 0, 0, &ss1, &ss2, CHECK_TYPE_RANGE, CHECK_REJECT));
357
358 printf("Testing Range blocked - this should NOT get through\n");
359 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "0Range blocked - this should NOT get through"));
360
361 // Unblock and check again
362 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 0, seconds, logfds[0], stdout));
363 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 0, seconds, logfds[0], stdout));
364 FAIL_ON_ERR_THR(knet_link_rm_acl(knet_h[1], 2, 0, &ss1, &ss2, CHECK_TYPE_RANGE, CHECK_REJECT));
365 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 1, seconds, logfds[0], stdout));
366 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 1, seconds, logfds[0], stdout));
367
368 printf("Testing Range unblocked - this should get through\n");
369 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "1Range unblocked - this should get through"));
370 FAIL_ON_ERR_THR(wait_for_reply(seconds));
371
372 // Finish up - disable ACLS to make sure the QUIT message gets through
373 FAIL_ON_ERR_THR(knet_handle_enable_access_lists(knet_h[1], 0));
374 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[1], TESTNODES, 1, seconds, logfds[0], stdout));
375 FAIL_ON_ERR_THR(wait_for_nodes_state(knet_h[2], TESTNODES, 1, seconds, logfds[0], stdout));
376
377 FAIL_ON_ERR_THR(knet_send_str(knet_h[2], "QUIT"));
378
379 // Check return from the receiving thread
380 pthread_join(recv_thread, (void**)&thread_err);
381 if (*thread_err) {
382 printf("Thread returned %d\n", *thread_err);
383 clean_exit(knet_h, TESTNODES, logfds, FAIL);
384 }
385
386 if (msgs_recvd != CORRECT_NUM_MSGS) {
387 printf("*** FAIL Recv thread got %d messages, expected %d\n", msgs_recvd, CORRECT_NUM_MSGS);
388 clean_exit(knet_h, TESTNODES, logfds, FAIL);
389 }
390 clean_exit(knet_h, TESTNODES, logfds, PASS);
391 }
392
393 int main(int argc, char *argv[])
394 {
395 printf("Testing with UDP\n");
396 test(KNET_TRANSPORT_UDP);
397
398 #ifdef HAVE_NETINET_SCTP_H
399 printf("Testing with SCTP currently disabled\n");
400 //test(KNET_TRANSPORT_SCTP);
401 #endif
402
403 return PASS;
404 }
405