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