1*eabc0478Schristos /* $NetBSD: socket.c,v 1.7 2024/08/18 20:47:13 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * socket.c - low-level socket operations 58585484eSchristos */ 68585484eSchristos 78585484eSchristos #ifdef HAVE_CONFIG_H 88585484eSchristos # include <config.h> 98585484eSchristos #endif 108585484eSchristos 118585484eSchristos #include <stdio.h> 128585484eSchristos 138585484eSchristos #include "ntp.h" 148585484eSchristos #include "ntp_io.h" 158585484eSchristos #include "ntp_net.h" 168585484eSchristos #include "ntp_debug.h" 178585484eSchristos 188585484eSchristos /* 198585484eSchristos * Windows C runtime ioctl() can't deal properly with sockets, 208585484eSchristos * map to ioctlsocket for this source file. 218585484eSchristos */ 228585484eSchristos #ifdef SYS_WINNT 238585484eSchristos #define ioctl(fd, opt, val) ioctlsocket(fd, opt, (u_long *)(val)) 248585484eSchristos #endif 258585484eSchristos 268585484eSchristos /* 278585484eSchristos * on Unix systems the stdio library typically 288585484eSchristos * makes use of file descriptors in the lower 298585484eSchristos * integer range. stdio usually will make use 308585484eSchristos * of the file descriptors in the range of 318585484eSchristos * [0..FOPEN_MAX) 328585484eSchristos * in order to keep this range clean, for socket 338585484eSchristos * file descriptors we attempt to move them above 348585484eSchristos * FOPEN_MAX. This is not as easy as it sounds as 358585484eSchristos * FOPEN_MAX changes from implementation to implementation 368585484eSchristos * and may exceed to current file decriptor limits. 378585484eSchristos * We are using following strategy: 388585484eSchristos * - keep a current socket fd boundary initialized with 398585484eSchristos * max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX)) 408585484eSchristos * - attempt to move the descriptor to the boundary or 418585484eSchristos * above. 428585484eSchristos * - if that fails and boundary > 0 set boundary 438585484eSchristos * to min(0, socket_fd_boundary - FD_CHUNK) 448585484eSchristos * -> retry 458585484eSchristos * if failure and boundary == 0 return old fd 468585484eSchristos * - on success close old fd return new fd 478585484eSchristos * 488585484eSchristos * effects: 498585484eSchristos * - fds will be moved above the socket fd boundary 508585484eSchristos * if at all possible. 518585484eSchristos * - the socket boundary will be reduced until 528585484eSchristos * allocation is possible or 0 is reached - at this 538585484eSchristos * point the algrithm will be disabled 548585484eSchristos */ 558585484eSchristos SOCKET 568585484eSchristos move_fd( 578585484eSchristos SOCKET fd 588585484eSchristos ) 598585484eSchristos { 608585484eSchristos #if !defined(SYS_WINNT) && defined(F_DUPFD) 618585484eSchristos #ifndef FD_CHUNK 628585484eSchristos #define FD_CHUNK 10 638585484eSchristos #endif 648585484eSchristos #ifndef FOPEN_MAX 658585484eSchristos #define FOPEN_MAX 20 668585484eSchristos #endif 678585484eSchristos /* 688585484eSchristos * number of fds we would like to have for 698585484eSchristos * stdio FILE* available. 708585484eSchristos * we can pick a "low" number as our use of 718585484eSchristos * FILE* is limited to log files and temporarily 728585484eSchristos * to data and config files. Except for log files 738585484eSchristos * we don't keep the other FILE* open beyond the 748585484eSchristos * scope of the function that opened it. 758585484eSchristos */ 768585484eSchristos #ifndef FD_PREFERRED_SOCKBOUNDARY 778585484eSchristos #define FD_PREFERRED_SOCKBOUNDARY 48 788585484eSchristos #endif 798585484eSchristos 808585484eSchristos static SOCKET socket_boundary = -1; 818585484eSchristos SOCKET newfd; 828585484eSchristos 83af12ab5eSchristos REQUIRE((int)fd >= 0); 848585484eSchristos 858585484eSchristos /* 868585484eSchristos * check whether boundary has be set up 878585484eSchristos * already 888585484eSchristos */ 898585484eSchristos if (socket_boundary == -1) { 908585484eSchristos socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK, 918585484eSchristos min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); 928585484eSchristos TRACE(1, ("move_fd: estimated max descriptors: %d, " 938585484eSchristos "initial socket boundary: %d\n", 948585484eSchristos GETDTABLESIZE(), socket_boundary)); 958585484eSchristos } 968585484eSchristos 978585484eSchristos /* 988585484eSchristos * Leave a space for stdio to work in. potentially moving the 998585484eSchristos * socket_boundary lower until allocation succeeds. 1008585484eSchristos */ 1018585484eSchristos do { 1028585484eSchristos if (fd >= 0 && fd < socket_boundary) { 1038585484eSchristos /* inside reserved range: attempt to move fd */ 1048585484eSchristos newfd = fcntl(fd, F_DUPFD, socket_boundary); 1058585484eSchristos 1068585484eSchristos if (newfd != -1) { 1078585484eSchristos /* success: drop the old one - return the new one */ 1088585484eSchristos close(fd); 1098585484eSchristos return newfd; 1108585484eSchristos } 1118585484eSchristos } else { 1128585484eSchristos /* outside reserved range: no work - return the original one */ 1138585484eSchristos return fd; 1148585484eSchristos } 1158585484eSchristos socket_boundary = max(0, socket_boundary - FD_CHUNK); 1168585484eSchristos TRACE(1, ("move_fd: selecting new socket boundary: %d\n", 1178585484eSchristos socket_boundary)); 1188585484eSchristos } while (socket_boundary > 0); 1198585484eSchristos #else 120af12ab5eSchristos ENSURE((int)fd >= 0); 1218585484eSchristos #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */ 1228585484eSchristos return fd; 1238585484eSchristos } 1248585484eSchristos 1258585484eSchristos 1268585484eSchristos /* 1278585484eSchristos * make_socket_nonblocking() - set up descriptor to be non blocking 1288585484eSchristos */ 1298585484eSchristos void 1308585484eSchristos make_socket_nonblocking( 1318585484eSchristos SOCKET fd 1328585484eSchristos ) 1338585484eSchristos { 1348585484eSchristos /* 1358585484eSchristos * set non-blocking, 1368585484eSchristos */ 1378585484eSchristos 1388585484eSchristos #ifdef USE_FIONBIO 1398585484eSchristos /* in vxWorks we use FIONBIO, but the others are defined for old 1408585484eSchristos * systems, so all hell breaks loose if we leave them defined 1418585484eSchristos */ 1428585484eSchristos #undef O_NONBLOCK 1438585484eSchristos #undef FNDELAY 1448585484eSchristos #undef O_NDELAY 1458585484eSchristos #endif 1468585484eSchristos 1478585484eSchristos #if defined(O_NONBLOCK) /* POSIX */ 1488585484eSchristos if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { 1498585484eSchristos msyslog(LOG_ERR, 1508585484eSchristos "fcntl(O_NONBLOCK) fails on fd #%d: %m", fd); 1518585484eSchristos exit(1); 1528585484eSchristos } 1538585484eSchristos #elif defined(FNDELAY) 1548585484eSchristos if (fcntl(fd, F_SETFL, FNDELAY) < 0) { 1558585484eSchristos msyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m", 1568585484eSchristos fd); 1578585484eSchristos exit(1); 1588585484eSchristos } 1598585484eSchristos #elif defined(O_NDELAY) /* generally the same as FNDELAY */ 1608585484eSchristos if (fcntl(fd, F_SETFL, O_NDELAY) < 0) { 1618585484eSchristos msyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m", 1628585484eSchristos fd); 1638585484eSchristos exit(1); 1648585484eSchristos } 1658585484eSchristos #elif defined(FIONBIO) 1668585484eSchristos { 1678585484eSchristos int on = 1; 1688585484eSchristos 1698585484eSchristos if (ioctl(fd, FIONBIO, &on) < 0) { 1708585484eSchristos msyslog(LOG_ERR, 1718585484eSchristos "ioctl(FIONBIO) fails on fd #%d: %m", 1728585484eSchristos fd); 1738585484eSchristos exit(1); 1748585484eSchristos } 1758585484eSchristos } 1768585484eSchristos #elif defined(FIOSNBIO) 1778585484eSchristos if (ioctl(fd, FIOSNBIO, &on) < 0) { 1788585484eSchristos msyslog(LOG_ERR, 1798585484eSchristos "ioctl(FIOSNBIO) fails on fd #%d: %m", fd); 1808585484eSchristos exit(1); 1818585484eSchristos } 1828585484eSchristos #else 1838585484eSchristos # include "Bletch: Need non-blocking I/O!" 1848585484eSchristos #endif 1858585484eSchristos } 1868585484eSchristos 1878585484eSchristos #if 0 1888585484eSchristos 1898585484eSchristos /* The following subroutines should probably be moved here */ 1908585484eSchristos 1918585484eSchristos static SOCKET 1928585484eSchristos open_socket( 1938585484eSchristos sockaddr_u * addr, 1948585484eSchristos int bcast, 1958585484eSchristos int turn_off_reuse, 1968585484eSchristos endpt * interf 1978585484eSchristos ) 1988585484eSchristos void 1998585484eSchristos sendpkt( 2008585484eSchristos sockaddr_u * dest, 201*eabc0478Schristos endpt * ep, 2028585484eSchristos int ttl, 2038585484eSchristos struct pkt * pkt, 2048585484eSchristos int len 2058585484eSchristos ) 2068585484eSchristos 2078585484eSchristos static inline int 2088585484eSchristos read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts) 2098585484eSchristos 2108585484eSchristos static inline int 2118585484eSchristos read_network_packet( 2128585484eSchristos SOCKET fd, 213*eabc0478Schristos endpt * itf, 2148585484eSchristos l_fp ts 2158585484eSchristos ) 2168585484eSchristos 2178585484eSchristos void 2188585484eSchristos kill_asyncio(int startfd) 2198585484eSchristos 2208585484eSchristos #endif /* 0 */ 221