summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-08-04 20:09:38 -0400
committerIan Moffett <ian@osmora.org>2025-08-04 20:10:50 -0400
commitff68e8679d7d2487126ff355ed2b34a6f84058bb (patch)
tree510cec60308d737f386bc9019c1389cf5dcd885d
parent8c9ef7128ff90522949b453087367b6b60e9587d (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>
-rw-r--r--sys/include/sys/socket.h16
-rw-r--r--sys/kern/kern_socket.c124
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