diff options
author | Ian Moffett <ian@osmora.org> | 2025-08-04 20:09:38 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-08-04 20:10:50 -0400 |
commit | ff68e8679d7d2487126ff355ed2b34a6f84058bb (patch) | |
tree | 510cec60308d737f386bc9019c1389cf5dcd885d /sys | |
parent | 8c9ef7128ff90522949b453087367b6b60e9587d (diff) |
kernel: socket: Add POSIX setsockopt()
This commit introduces the kernel-side implementation of setsockopt()
for setting options on a socket file descriptor. See POSIX
setsockopt() for more information.
https://pubs.opengroup.org/onlinepubs/9690949499/functions/setsockopt.html
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys')
-rw-r--r-- | sys/include/sys/socket.h | 16 | ||||
-rw-r--r-- | sys/kern/kern_socket.c | 124 |
2 files changed, 140 insertions, 0 deletions
diff --git a/sys/include/sys/socket.h b/sys/include/sys/socket.h index c82ae4e..9224542 100644 --- a/sys/include/sys/socket.h +++ b/sys/include/sys/socket.h @@ -69,6 +69,10 @@ typedef uint32_t socklen_t; /* Socket types */ #define SOCK_STREAM 1 +/* Socket option names */ +#define SO_RCVTIMEO 0 /* Max time recv(2) waits */ +#define _SO_MAX 1 /* Max socket options */ + struct sockaddr_un { sa_family_t sun_family; char sun_path[108]; @@ -149,12 +153,22 @@ struct cmsg_list { uint8_t is_init : 1; }; +/* + * Socket option that may be applied to + * sockets on the system. + */ +struct sockopt { + socklen_t len; + char data[]; +}; + struct ksocket { int sockfd; union { struct sockaddr sockaddr; struct sockaddr_un un; }; + struct sockopt *opt[_SO_MAX]; struct proc *owner; struct cmsg_list cmsg_list; struct sockbuf buf; @@ -174,6 +188,8 @@ scret_t sys_sendmsg(struct syscall_args *scargs); int socket(int domain, int type, int protocol); int bind(int sockfd, const struct sockaddr *addr, socklen_t len); + +int setsockopt(int sockfd, int level, int name, const void *v, socklen_t len); int connect(int sockfd, const struct sockaddr *addr, socklen_t len); ssize_t send(int sockfd, const void *buf, size_t size, int flags); diff --git a/sys/kern/kern_socket.c b/sys/kern/kern_socket.c index 02c58be..55b7668 100644 --- a/sys/kern/kern_socket.c +++ b/sys/kern/kern_socket.c @@ -31,6 +31,7 @@ #include <sys/sio.h> #include <sys/systm.h> #include <sys/proc.h> +#include <sys/time.h> #include <sys/namei.h> #include <sys/sched.h> #include <sys/errno.h> @@ -47,6 +48,17 @@ static struct vops socket_vops; /* + * This table maps socket option names to + * lengths of their underlying structure. + * + * This is used for bounds/length checking within + * setsockopt() + */ +static size_t sockopt_lentab[_SO_MAX] = { + [ SO_RCVTIMEO ] = sizeof(struct timeval) +}; + +/* * Get a kernel socket structure from a * file descriptor. * @@ -101,6 +113,7 @@ static int socket_reclaim(struct vnode *vp) { struct ksocket *ksock; + struct sockopt *opt; /* Is this even a socket? */ if (vp->type != VSOCK) { @@ -112,6 +125,15 @@ socket_reclaim(struct vnode *vp) return -EIO; } + /* Free up any used options */ + for (int i = 0; i < _SO_MAX; ++i) { + opt = ksock->opt[i]; + if (opt != NULL) { + dynfree(opt); + ksock->opt[i] = NULL; + } + } + fd_close(ksock->sockfd); mutex_free(ksock->mtx); dynfree(ksock); @@ -185,6 +207,46 @@ connect_domain(int sockfd, struct ksocket *ksock, struct sockaddr_un *un) } /* + * Wait until data is received for the + * recv() function. + * + * @sockfd: Socket we are waiting on + * + * Returns zero on success, otherwise a less + * than zero value is returned. + */ +static int +socket_rx_wait(int sockfd) +{ + struct ksocket *ksock; + struct sockopt *opt; + struct timeval tv; + int error; + + if (ksock == NULL) { + return -EINVAL; + } + + error = get_ksock(sockfd, &ksock); + if (error < 0) { + return error; + } + + /* + * If the socket does not have this option set, + * we will assume that there is no timeout value. + */ + opt = ksock->opt[SO_RCVTIMEO]; + if (opt == NULL) { + return -1; + } + + memcpy(&tv, opt->data, opt->len); + sched_suspend(NULL, &tv); + return 0; +} + +/* * Send data to socket - POSIX send(2) core * * @sockfd: File descriptor that backs this socket @@ -397,6 +459,68 @@ bind(int sockfd, const struct sockaddr *addr, socklen_t len) } /* + * Set socket options - POSIX setsockopt(3) core + * + * @sockfd: File descriptor of socket + * @level: Protocol level + * @v: Options value + * @len: Length of data pointed to by 'v' + */ +int +setsockopt(int sockfd, int level, int name, const void *v, socklen_t len) +{ + struct ksocket *ksock; + struct sockopt *opt; + size_t exp_len; + int error; + + /* Must have a valid fd */ + if (sockfd < 0) { + return -EBADF; + } + + /* Ensure value and length are valid */ + if (v == NULL || len == 0) { + return -EINVAL; + } + + /* Verify the name */ + if (name >= _SO_MAX) { + return -EINVAL; + } + + /* Grab a new socket */ + if ((error = get_ksock(sockfd, &ksock)) < 0) { + return error; + } + + /* Clamp the input length as needed */ + exp_len = sockopt_lentab[name]; + if (len > exp_len) { + len = exp_len; + } + + /* + * Here we will grab the socket options. If it is + * NULL, we'll need to allocate one. + */ + if ((opt = ksock->opt[name]) == NULL) { + opt = dynalloc(sizeof(*opt) + len); + + if (opt == NULL) { + return -ENOMEM; + } + + opt->len = len; + ksock->opt[name] = opt; + } + + memcpy(opt->data, v, len); + opt->len = len; + return 0; +} + +/* * Connect to a socket * * @sockfd: File descriptor to connect |