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, &notify_fn));
278  		FAIL_ON_ERR_THR(knet_handle_enable_sock_notify(knet_h[2], 0, &notify_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