1    	/*
2    	 * Copyright (C) 2016-2025 Red Hat, Inc.  All rights reserved.
3    	 *
4    	 * Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
5    	 *
6    	 * This software licensed under LGPL-2.0+
7    	 */
8    	
9    	#include "config.h"
10   	
11   	#include <unistd.h>
12   	#include <string.h>
13   	#include <errno.h>
14   	#include <pthread.h>
15   	#include <sys/types.h>
16   	#include <sys/socket.h>
17   	#include <netinet/in.h>
18   	#include <netinet/ip.h>
19   	
20   	#include "libknet.h"
21   	#include "compat.h"
22   	#include "host.h"
23   	#include "link.h"
24   	#include "logging.h"
25   	#include "common.h"
26   	#include "transport_common.h"
27   	
28   	/*
29   	 * reuse Jan Friesse's compat layer as wrapper to drop usage of sendmmsg
30   	 *
31   	 * TODO: kill those wrappers once we work on packet delivery guarantees
32   	 */
33   	
34   	int _recvmmsg(int sockfd, struct knet_mmsghdr *msgvec, unsigned int vlen, unsigned int flags)
35   	{
36   		int savederrno = 0, err = 0;
37   		unsigned int i;
38   	
39   		for (i = 0; i < vlen; i++) {
40   			err = recvmsg(sockfd, &msgvec[i].msg_hdr, flags);
41   			savederrno = errno;
42   			if (err >= 0) {
43   				msgvec[i].msg_len = err;
44   				if (err == 0) {
45   					/* No point in reading anything more until we know this has been dealt with
46   					   or we'll just get a vector full of them. Several in fact */
47   					i++;
48   					break;
49   				}
50   			} else {
51   				if ((i > 0) &&
52   				    ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
53   					savederrno = 0;
54   				}
55   				break;
56   			}
57   		}
58   	
59   		errno = savederrno;
60   		return ((i > 0) ? (int)i : err);
61   	}
62   	
63   	int _sendmmsg(int sockfd, int connection_oriented, struct knet_mmsghdr *msgvec, unsigned int vlen, unsigned int flags)
64   	{
65   		int savederrno = 0, err = 0;
66   		unsigned int i;
67   		struct msghdr temp_msg;
68   		struct msghdr *use_msghdr;
69   	
(1) Event loop_bound_upper: Using tainted expression "vlen" as a loop boundary.
70   		for (i = 0; i < vlen; i++) {
71   			if (connection_oriented == TRANSPORT_PROTO_IS_CONNECTION_ORIENTED) {
72   				memcpy(&temp_msg, &msgvec[i].msg_hdr, sizeof(struct msghdr));
73   				temp_msg.msg_name = NULL;
74   				temp_msg.msg_namelen = 0;
75   				use_msghdr = &temp_msg;
76   			} else {
77   				use_msghdr = &msgvec[i].msg_hdr;
78   			}
79   			err = sendmsg(sockfd, use_msghdr, flags);
80   			savederrno = errno;
81   			if (err < 0) {
82   				break;
83   			}
84   		}
85   	
86   		errno = savederrno;
87   		return ((i > 0) ? (int)i : err);
88   	}
89   	
90   	/* Assume neither of these constants can ever be zero */
91   	#ifndef SO_RCVBUFFORCE
92   	#define SO_RCVBUFFORCE 0
93   	#endif
94   	#ifndef SO_SNDBUFFORCE
95   	#define SO_SNDBUFFORCE 0
96   	#endif
97   	
98   	static int _configure_sockbuf(knet_handle_t knet_h, int sock, int option, int force, int target)
99   	{
100  		int savederrno = 0;
101  		int new_value;
102  		socklen_t value_len = sizeof new_value;
103  	
104  		if (setsockopt(sock, SOL_SOCKET, option, &target, sizeof target) != 0) {
105  			savederrno = errno;
106  			log_err(knet_h, KNET_SUB_TRANSPORT,
107  				"Error setting socket buffer via option %d to value %d: %s\n",
108  				option, target, strerror(savederrno));
109  			errno = savederrno;
110  			return -1;
111  		}
112  	
113  		if (getsockopt(sock, SOL_SOCKET, option, &new_value, &value_len) != 0) {
114  			savederrno = errno;
115  			log_err(knet_h, KNET_SUB_TRANSPORT,
116  				"Error getting socket buffer via option %d: %s\n",
117  				option, strerror(savederrno));
118  			errno = savederrno;
119  			return -1;
120  		}
121  	
122  		if (value_len != sizeof new_value) {
123  			log_err(knet_h, KNET_SUB_TRANSPORT,
124  				"Socket option %d returned unexpected size %u\n",
125  				option, value_len);
126  			errno = ERANGE;
127  			return -1;
128  		}
129  	
130  		if (target <= new_value) {
131  			return 0;
132  		}
133  	
134  		if (!force || !(knet_h->flags & KNET_HANDLE_FLAG_PRIVILEGED)) {
135  			log_err(knet_h, KNET_SUB_TRANSPORT,
136  				"Failed to set socket buffer via option %d to value %d: capped at %d",
137  				option, target, new_value);
138  			if (!(knet_h->flags & KNET_HANDLE_FLAG_PRIVILEGED)) {
139  				log_err(knet_h, KNET_SUB_TRANSPORT,
140  					"Continuing regardless, as the handle is not privileged."
141  					" Expect poor performance!");
142  				return 0;
143  			} else {
144  				errno = ENAMETOOLONG;
145  				return -1;
146  			}
147  		}
148  	
149  		if (setsockopt(sock, SOL_SOCKET, force, &target, sizeof target) < 0) {
150  			savederrno = errno;
151  			log_err(knet_h, KNET_SUB_TRANSPORT,
152  				"Failed to set socket buffer via force option %d: %s",
153  				force, strerror(savederrno));
154  			if (savederrno == EPERM) {
155  				errno = ENAMETOOLONG;
156  			} else {
157  				errno = savederrno;
158  			}
159  			return -1;
160  		}
161  	
162  		return 0;
163  	}
164  	
165  	int _configure_common_socket(knet_handle_t knet_h, int sock, uint64_t flags, const char *type)
166  	{
167  		int err = 0, savederrno = 0;
168  	
169  		if (_fdset_cloexec(sock)) {
170  			savederrno = errno;
171  			err = -1;
172  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s CLOEXEC socket opts: %s",
173  				type, strerror(savederrno));
174  			goto exit_error;
175  		}
176  	
177  		if (_fdset_nonblock(sock)) {
178  			savederrno = errno;
179  			err = -1;
180  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s NONBLOCK socket opts: %s",
181  				type, strerror(savederrno));
182  			goto exit_error;
183  		}
184  	
185  		if (_configure_sockbuf(knet_h, sock, SO_RCVBUF, SO_RCVBUFFORCE, KNET_RING_RCVBUFF)) {
186  			savederrno = errno;
187  			err = -1;
188  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s receive buffer: %s",
189  				type, strerror(savederrno));
190  			goto exit_error;
191  		}
192  	
193  		if (_configure_sockbuf(knet_h, sock, SO_SNDBUF, SO_SNDBUFFORCE, KNET_RING_RCVBUFF)) {
194  			savederrno = errno;
195  			err = -1;
196  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s send buffer: %s",
197  				type, strerror(savederrno));
198  			goto exit_error;
199  		}
200  	
201  		if (flags & KNET_LINK_FLAG_TRAFFICHIPRIO) {
202  	#ifdef KNET_LINUX
203  	#ifdef SO_PRIORITY
204  			int value = 6; /* TC_PRIO_INTERACTIVE */
205  	
206  			if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &value, sizeof(value)) < 0) {
207  				savederrno = errno;
208  				err = -1;
209  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s priority: %s",
210  					type, strerror(savederrno));
211  				goto exit_error;
212  			}
213  			log_debug(knet_h, KNET_SUB_TRANSPORT, "TC_PRIO_INTERACTIVE enabled on socket: %i", sock);
214  	#else
215  			log_debug(knet_h, KNET_SUB_TRANSPORT, "TC_PRIO_INTERACTIVE not available in this build/platform");
216  	#endif
217  	#endif
218  	#if defined(IP_TOS)
219  			if (knet_h->prio_dscp) {
220  				/* dscp is the 6 highest bits of TOS IP header field, RFC 2474 */
221  				int value = (knet_h->prio_dscp & 0x3f) << 2;
222  	
223  				if (setsockopt(sock, IPPROTO_IP, IP_TOS, &value, sizeof(value)) < 0) {
224  					savederrno = errno;
225  					err = -1;
226  					log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s priority: %s",
227  						type, strerror(savederrno));
228  					goto exit_error;
229  				}
230  				log_debug(knet_h, KNET_SUB_TRANSPORT, "dscp %d set on socket: %i", knet_h->prio_dscp, sock);
231  			} else {
232  	#if defined(IPTOS_LOWDELAY)
233  				int value = IPTOS_LOWDELAY;
234  	
235  				if (setsockopt(sock, IPPROTO_IP, IP_TOS, &value, sizeof(value)) < 0) {
236  					savederrno = errno;
237  					err = -1;
238  					log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s priority: %s",
239  						type, strerror(savederrno));
240  					goto exit_error;
241  				}
242  				log_debug(knet_h, KNET_SUB_TRANSPORT, "IPTOS_LOWDELAY enabled on socket: %i", sock);
243  	#else
244  				log_debug(knet_h, KNET_SUB_TRANSPORT, "IPTOS_LOWDELAY not available in this build/platform");
245  	#endif
246  			}
247  	#endif
248  		}
249  	
250  	exit_error:
251  		errno = savederrno;
252  		return err;
253  	}
254  	
255  	int _configure_transport_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, uint64_t flags, const char *type)
256  	{
257  		int err = 0, savederrno = 0;
258  		int value;
259  	
260  		if (_configure_common_socket(knet_h, sock, flags, type) < 0) {
261  			savederrno = errno;
262  			err = -1;
263  			goto exit_error;
264  		}
265  	
266  	#ifdef KNET_LINUX
267  	#ifdef IP_FREEBIND
268  		value = 1;
269  		if (setsockopt(sock, SOL_IP, IP_FREEBIND, &value, sizeof(value)) <0) {
270  			savederrno = errno;
271  			err = -1;
272  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set FREEBIND on %s socket: %s",
273  				type, strerror(savederrno));
274  			goto exit_error;
275  		}
276  		log_debug(knet_h, KNET_SUB_TRANSPORT, "FREEBIND enabled on socket: %i", sock);
277  	#else
278  		log_debug(knet_h, KNET_SUB_TRANSPORT, "FREEBIND not available in this build/platform");
279  	#endif
280  	#endif
281  	#ifdef KNET_BSD
282  	#ifdef IP_BINDANY /* BSD */
283  		value = 1;
284  		if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &value, sizeof(value)) <0) {
285  			savederrno = errno;
286  			err = -1;
287  			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set BINDANY on %s socket: %s",
288  				type, strerror(savederrno));
289  			goto exit_error;
290  		}
291  		log_debug(knet_h, KNET_SUB_TRANSPORT, "BINDANY enabled on socket: %i", sock);
292  	#else
293  		log_debug(knet_h, KNET_SUB_TRANSPORT, "BINDANY not available in this build/platform");
294  	#endif
295  	#endif
296  	
297  		if (address->ss_family == AF_INET6) {
298  			value = 1;
299  			if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
300  				       &value, sizeof(value)) < 0) {
301  				savederrno = errno;
302  				err = -1;
303  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s IPv6 only: %s",
304  					type, strerror(savederrno));
305  				goto exit_error;
306  	
307  			}
308  	#ifdef KNET_LINUX
309  	#ifdef IPV6_MTU_DISCOVER
310  			value = IPV6_PMTUDISC_PROBE;
311  			if (setsockopt(sock, SOL_IPV6, IPV6_MTU_DISCOVER, &value, sizeof(value)) <0) {
312  				savederrno = errno;
313  				err = -1;
314  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set PMTUDISC on %s socket: %s",
315  					type, strerror(savederrno));
316  				goto exit_error;
317  			}
318  			log_debug(knet_h, KNET_SUB_TRANSPORT, "IPV6_MTU_DISCOVER enabled on socket: %i", sock);
319  	#else
320  			log_debug(knet_h, KNET_SUB_TRANSPORT, "IPV6_MTU_DISCOVER not available in this build/platform");
321  	#endif
322  	#endif
323  	#ifdef IPV6_DONTFRAG
324  			value = 1;
325  			if (setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, &value, sizeof(value)) <0) {
326  				savederrno = errno;
327  				err = -1;
328  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set DONTFRAG on %s socket: %s",
329  					type, strerror(savederrno));
330  				goto exit_error;
331  			}
332  			log_debug(knet_h, KNET_SUB_TRANSPORT, "IPV6_DONTFRAG enabled on socket: %i", sock);
333  	#else
334  			log_debug(knet_h, KNET_SUB_TRANSPORT, "IPV6_DONTFRAG not available in this build/platform");
335  	#endif
336  		} else {
337  	#ifdef KNET_LINUX
338  	#ifdef IP_MTU_DISCOVER
339  			value = IP_PMTUDISC_PROBE;
340  			if (setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, &value, sizeof(value)) <0) {
341  				savederrno = errno;
342  				err = -1;
343  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set PMTUDISC on %s socket: %s",
344  					type, strerror(savederrno));
345  				goto exit_error;
346  			}
347  			log_debug(knet_h, KNET_SUB_TRANSPORT, "PMTUDISC enabled on socket: %i", sock);
348  	#else
349  			log_debug(knet_h, KNET_SUB_TRANSPORT, "PMTUDISC not available in this build/platform");
350  	#endif
351  	#endif
352  	#if defined(KNET_BSD) || defined(KNET_SOLARIS)
353  	#ifdef IP_DONTFRAG
354  			value = 1;
355  			if (setsockopt(sock, IPPROTO_IP, IP_DONTFRAG, &value, sizeof(value)) <0) {
356  				savederrno = errno;
357  				err = -1;
358  				log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set DONTFRAG on %s socket: %s",
359  					type, strerror(savederrno));
360  				goto exit_error;
361  			}
362  			log_debug(knet_h, KNET_SUB_TRANSPORT, "DONTFRAG enabled on socket: %i", sock);
363  	#else
364  			log_debug(knet_h, KNET_SUB_TRANSPORT, "DONTFRAG not available in this build/platform");
365  	#endif
366  	#endif
367  		}
368  	
369  	exit_error:
370  		errno = savederrno;
371  		return err;
372  	}
373  	
374  	int _init_socketpair(knet_handle_t knet_h, int *sock)
375  	{
376  		int err = 0, savederrno = 0;
377  		int i;
378  	
379  		if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) != 0) {
380  			savederrno = errno;
381  			err = -1;
382  			log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize socketpair: %s",
383  				strerror(savederrno));
384  			goto exit_fail;
385  		}
386  	
387  		for (i = 0; i < 2; i++) {
388  			if (_configure_common_socket(knet_h, sock[i], 0, "local socketpair") < 0) {
389  				savederrno = errno;
390  				err = -1;
391  				goto exit_fail;
392  			}
393  		}
394  	
395  	exit_fail:
396  		errno = savederrno;
397  		return err;
398  	}
399  	
400  	void _close_socketpair(knet_handle_t knet_h, int *sock)
401  	{
402  		int i;
403  	
404  		for (i = 0; i < 2; i++) {
405  			if (sock[i]) {
406  				close(sock[i]);
407  				sock[i] = 0;
408  			}
409  		}
410  	}
411  	
412  	/*
413  	 * must be called with global read lock
414  	 *
415  	 * return -1 on error
416  	 * return 0 if fd is invalid
417  	 * return 1 if fd is valid
418  	 */
419  	int _is_valid_fd(knet_handle_t knet_h, int sockfd)
420  	{
421  		int ret = 0;
422  	
423  		if (sockfd < 0) {
424  			errno = EINVAL;
425  			return -1;
426  		}
427  	
428  		if (sockfd >= KNET_MAX_FDS) {
429  			errno = EINVAL;
430  			return -1;
431  		}
432  	
433  		if (knet_h->knet_transport_fd_tracker[sockfd].transport >= KNET_MAX_TRANSPORTS) {
434  			ret = 0;
435  		} else {
436  			ret = 1;
437  		}
438  	
439  		return ret;
440  	}
441  	
442  	/*
443  	 * must be called with global write lock
444  	 */
445  	
446  	int _set_fd_tracker(knet_handle_t knet_h, int sockfd, uint8_t transport, uint8_t data_type, socklen_t socklen, void *data, int ifindex)
447  	{
448  		if (sockfd < 0) {
449  			errno = EINVAL;
450  			return -1;
451  		}
452  	
453  		if (sockfd >= KNET_MAX_FDS) {
454  			errno = EINVAL;
455  			return -1;
456  		}
457  	
458  		knet_h->knet_transport_fd_tracker[sockfd].transport = transport;
459  		knet_h->knet_transport_fd_tracker[sockfd].data_type = data_type;
460  		knet_h->knet_transport_fd_tracker[sockfd].sockaddr_len = socklen;
461  		knet_h->knet_transport_fd_tracker[sockfd].data = data;
462  		knet_h->knet_transport_fd_tracker[sockfd].ifindex = ifindex;
463  	
464  		return 0;
465  	}
466