From 4522b6c2da0712e82e90bf433a03fd900ebfa04f Mon Sep 17 00:00:00 2001 From: Matheus Date: Tue, 18 Oct 2022 00:01:06 -0300 Subject: [PATCH] sockets: initial impl --- src/arch/x86_64/syscall.asm | 12 +- src/fs/tmpfs.c | 23 +++- src/fs/vfs.c | 39 +++++- src/include/kernel/socket.h | 67 ++++++++++ src/include/kernel/unsocket.h | 16 +++ src/include/kernel/vfs.h | 4 +- src/sys/socket.c | 85 ++++++++++++ src/sys/syscalls/accept.c | 105 +++++++++++++++ src/sys/syscalls/bind.c | 44 ++++++ src/sys/syscalls/connect.c | 44 ++++++ src/sys/syscalls/listen.c | 38 ++++++ src/sys/syscalls/open.c | 5 + src/sys/syscalls/socket_syscall.c | 55 ++++++++ src/sys/unsocket.c | 213 ++++++++++++++++++++++++++++++ 14 files changed, 745 insertions(+), 5 deletions(-) create mode 100644 src/include/kernel/socket.h create mode 100644 src/include/kernel/unsocket.h create mode 100644 src/sys/socket.c create mode 100644 src/sys/syscalls/accept.c create mode 100644 src/sys/syscalls/bind.c create mode 100644 src/sys/syscalls/connect.c create mode 100644 src/sys/syscalls/listen.c create mode 100644 src/sys/syscalls/socket_syscall.c create mode 100644 src/sys/unsocket.c diff --git a/src/arch/x86_64/syscall.asm b/src/arch/x86_64/syscall.asm index 11fa0941..7b276bf3 100644 --- a/src/arch/x86_64/syscall.asm +++ b/src/arch/x86_64/syscall.asm @@ -35,7 +35,12 @@ extern syscall_openat extern syscall_chroot extern syscall_mkdirat extern syscall_clock_gettime -func_count equ 34 +extern syscall_socket +extern syscall_bind +extern syscall_listen +extern syscall_connect +extern syscall_accept +func_count equ 39 func_table: @@ -73,6 +78,11 @@ func_table: dq syscall_chroot dq syscall_mkdirat dq syscall_clock_gettime + dq syscall_socket + dq syscall_bind + dq syscall_listen + dq syscall_connect + dq syscall_accept section .text global asm_syscall_entry diff --git a/src/fs/tmpfs.c b/src/fs/tmpfs.c index cc4b26d2..1b7fe525 100644 --- a/src/fs/tmpfs.c +++ b/src/fs/tmpfs.c @@ -161,8 +161,29 @@ static int tmpfs_chmod(vnode_t* node, mode_t mode){ return 0; } +static int tmpfs_mksocket(dirnode_t* parent, char* name, mode_t mode){ + + vnode_t* node = vfs_newnode(name, parent->vnode.fs, NULL); + + if(!node) + return ENOMEM; + + if(!hashtable_insert(&parent->children, name, node)){ + vfs_destroynode(node); + return ENOMEM; + } + + stat* st = &node->st; + + st->st_mode = MAKETYPE(TYPE_SOCKET) | mode; + st->st_blksize = PAGE_SIZE; + st->st_ino = parent->vnode.fs->data++; + + return 0; + +} static fscalls_t funcs = { - tmpfs_mount, tmpfs_unmount, tmpfs_open, tmpfs_close, tmpfs_mkdir, tmpfs_create, tmpfs_write, tmpfs_read, tmpfs_getdirent, tmpfs_chmod + tmpfs_mount, tmpfs_unmount, tmpfs_open, tmpfs_close, tmpfs_mkdir, tmpfs_create, tmpfs_write, tmpfs_read, tmpfs_getdirent, tmpfs_chmod, tmpfs_mksocket }; diff --git a/src/fs/vfs.c b/src/fs/vfs.c index 7736f41d..05178a5e 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -206,6 +206,7 @@ int vfs_getdirent(dirnode_t* node, dent_t* buff, size_t count, uintmax_t offset, return err; } + int vfs_open(vnode_t** buff, dirnode_t* ref, char* path){ vnode_t* node; @@ -258,8 +259,43 @@ int vfs_create(dirnode_t* ref, char* path, mode_t mode){ return 0; } -int vfs_mkdir(dirnode_t* ref, char* path, mode_t mode){ +int vfs_mksocket(dirnode_t* ref, char* path, mode_t mode){ + + dirnode_t* parent = NULL; + dirnode_t* buff = NULL; + + char name[512]; + spinlock_acquire(&lock); + + int result = vfs_resolvepath(&buff, &parent, ref, path, name); + + if(buff){ + spinlock_release(&lock); + return EEXIST; + } + + if(!parent){ + spinlock_release(&lock); + return result; + } + + if(!parent->vnode.fs->calls->mksocket){ + spinlock_release(&lock); + return ENOSYS; + } + + result = parent->vnode.fs->calls->mksocket(parent, name, mode); + + spinlock_release(&lock); + + if(result) return result; + + return 0; +} + +int vfs_mkdir(dirnode_t* ref, char* path, mode_t mode){ + dirnode_t* parent = NULL; dirnode_t* buff = NULL; @@ -279,7 +315,6 @@ int vfs_mkdir(dirnode_t* ref, char* path, mode_t mode){ return result; } - result = parent->vnode.fs->calls->mkdir(parent, name, mode); spinlock_release(&lock); diff --git a/src/include/kernel/socket.h b/src/include/kernel/socket.h new file mode 100644 index 00000000..14b12ee8 --- /dev/null +++ b/src/include/kernel/socket.h @@ -0,0 +1,67 @@ +#ifndef _SOCKET_H_INCLUDE +#define _SOCKET_H_INCLUDE + +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET_STATE_UNBOUND 0 +#define SOCKET_STATE_BOUND 1 +#define SOCKET_STATE_LISTENING 2 +#define SOCKET_STATE_CONNECTED 3 + +#define SOCK_STREAM 1 +#define AF_UNIX 1 + +typedef struct _socket_t{ + int lock; + int family; + int type; + int protocol; + struct _socket_t* peer; + size_t backlogsize; + off_t backlogend; + struct _socket_t** backlog; + event_t connectevent; // when the socket is listening and this is fired, someone used connect() on the socket + event_t acceptevent; // when a socket is waiting for a connection to be accepted, it sleeps on this + int state; + union { + sockaddr_un addr_un; + }; + size_t addrlen; + ringbuffer_t buffer; +} socket_t; + +static inline int socket_addtobacklog(socket_t* listener, socket_t* client){ + if(listener->backlogend == listener->backlogsize) + return ECONNREFUSED; + + listener->backlog[listener->backlogend++] = client; + + return 0; + +} + +static inline socket_t* socket_popfrombacklog(socket_t* listener){ + + socket_t* sock = listener->backlog[0]; + listener->backlogend--; + + memcpy(listener->backlog, listener->backlog+1, sizeof(socket_t*)*listener->backlogend); + + listener->backlog[listener->backlogend]; + + return sock; +} + +int socket_connect(struct _socket_t* sock, void* addr, size_t addrlen); +int socket_new(socket_t** returnptr, int family, int type, int protocol); +int socket_bind(struct _socket_t* sock, void* addr, size_t addrlen); +int socket_listen(struct _socket_t* sock, int backlog); +int socket_accept(socket_t** peer, socket_t* sock, void* addr, size_t* addrlen); + +#endif diff --git a/src/include/kernel/unsocket.h b/src/include/kernel/unsocket.h new file mode 100644 index 00000000..a121eb1e --- /dev/null +++ b/src/include/kernel/unsocket.h @@ -0,0 +1,16 @@ +#ifndef _UNSOCKET_H_INCLUDE +#define _UNSOCKET_H_INCLUDE + +#include + +typedef struct { + short sun_family; + char sun_path[108]; +} sockaddr_un; + +struct _socket_t; + +int unsocket_new(struct _socket_t** returnptr, int type, int protocol); +int unsocket_bind(struct _socket_t* sock, sockaddr_un* addr, size_t addrlen); + +#endif diff --git a/src/include/kernel/vfs.h b/src/include/kernel/vfs.h index 3eaf517a..ae3a42be 100644 --- a/src/include/kernel/vfs.h +++ b/src/include/kernel/vfs.h @@ -63,9 +63,11 @@ typedef struct _fscalls_t { int (*getdirent)(dirnode_t* node, dent_t* buff, size_t count, uintmax_t offset, size_t* readcount); // change file mode int (*chmod)(vnode_t* node, mode_t mode); + // create socket in address + int (*mksocket)(dirnode_t* ref, char* path, mode_t mode); } fscalls_t; - +int vfs_mksocket(dirnode_t* ref, char* path, mode_t mode); int vfs_mount(dirnode_t* ref, char* device, char* mountpoint, char* fs, int mountflags, void* fsinfo); int vfs_umount(dirnode_t* ref, char* mountpoint); int vfs_open(vnode_t** buf, dirnode_t* ref, char* path); diff --git a/src/sys/socket.c b/src/sys/socket.c new file mode 100644 index 00000000..0ec17445 --- /dev/null +++ b/src/sys/socket.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + +int socket_new(struct _socket_t** returnptr, int family, int type, int protocol){ + + switch(family){ + case AF_UNIX: + return unsocket_new(returnptr, type, protocol); + break; + default: + return EAFNOSUPPORT; + } + +} + +// TODO move around locks and tests + +int socket_bind(struct _socket_t* sock, void* addr, size_t addrlen){ + + if(sock->addrlen) + return EINVAL; + + switch(sock->family){ + case AF_UNIX: + return unsocket_bind(sock, addr, addrlen); + default: + return EINVAL; + } +} + +int socket_listen(struct _socket_t* sock, int backlog){ + + // XXX check if the socket supports listening? + + int err = 0; + + spinlock_acquire(&sock->lock); + + // XXX limit backlog + + if(sock->state != SOCKET_STATE_BOUND || backlog == 0){ + err = EINVAL; + goto _ret; + } + + sock->backlog = alloc(backlog*sizeof(socket_t**)); + + if(!sock->backlog){ + err = ENOMEM; + goto _ret; + } + + sock->backlogsize = backlog; + sock->state = SOCKET_STATE_LISTENING; + + _ret: + + spinlock_release(&sock->lock); + + return err; + +} + +int socket_connect(struct _socket_t* sock, void* addr, size_t addrlen){ + + switch(sock->family){ + case AF_UNIX: + return unsocket_connect(sock, addr, addrlen); + default: + return EINVAL; + } + +} + +int socket_accept(socket_t** peer, socket_t* sock, void* addr, size_t* addrlen){ + switch(sock->family){ + case AF_UNIX: + return unsocket_accept(peer, sock, addr, addrlen); + default: + return EINVAL; + } +} diff --git a/src/sys/syscalls/accept.c b/src/sys/syscalls/accept.c new file mode 100644 index 00000000..db190f0e --- /dev/null +++ b/src/sys/syscalls/accept.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +extern fs_t kerneltmpfs; + +syscallret syscall_accept(int sockfd, void* addr, size_t* addrlen){ + + syscallret retv; + retv.ret = -1; + + if(addr > USER_SPACE_END){ + retv.errno = EFAULT; + return retv; + } + + proc_t* proc = arch_getcls()->thread->proc; + + // access sockfd + + fd_t* fd; + + int err = fd_access(&proc->fdtable, &fd, sockfd); + + if(err){ + retv.errno = err; + return retv; + } + + if(GETTYPE(fd->mode) != TYPE_SOCKET){ + retv.errno = ENOTSOCK; + goto _fail; + } + + socket_t* listenersock = fd->node->objdata; + + // create new fd + + fd_t* connfd = NULL; + int connifd; + + retv.errno = fd_alloc(&proc->fdtable, &connfd, &connifd, 0); + + if(retv.errno) + goto _fail; + + connfd->node = vfs_newnode("SOCKET", &kerneltmpfs, NULL); + if(!connfd->node){ + retv.errno = ENOMEM; + goto _fail; + } + connfd->mode = 0777 | MAKETYPE(TYPE_SOCKET); + connfd->node->refcount = 1; + connfd->node->st.st_mode = connfd->mode; + + // create socket for new fd + + socket_t* connsock; + + retv.errno = socket_new(&connsock, listenersock->family, listenersock->type, listenersock->protocol); + + // get peer + + socket_t* peer; + + retv.errno = socket_accept(&peer, listenersock, addr, addrlen); + + if(retv.errno) + goto _fail; + + connsock->peer = peer; + connsock->state = SOCKET_STATE_CONNECTED; + + spinlock_acquire(&peer->lock); + + peer->peer = connsock; + + event_signal(&peer->acceptevent, true); + + spinlock_release(&peer->lock); + + fd_release(fd); + + retv.ret = connifd; + + memcpy(addr, &peer->addr_un, peer->addrlen); + *addrlen = peer->addrlen; + + return retv; + + _fail: + + fd_release(fd); + + if(connfd){ + fd_release(connfd); + fd_free(&proc->fdtable, connifd); + } + + return retv; + + +} diff --git a/src/sys/syscalls/bind.c b/src/sys/syscalls/bind.c new file mode 100644 index 00000000..65677044 --- /dev/null +++ b/src/sys/syscalls/bind.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include + +syscallret syscall_bind(int sockfd, void* addr, size_t addrlen){ + + syscallret retv; + retv.ret = -1; + + if(addr > USER_SPACE_END){ + retv.errno = EFAULT; + return retv; + } + + proc_t* proc = arch_getcls()->thread->proc; + + fd_t* fd; + + int err = fd_access(&proc->fdtable, &fd, sockfd); + + if(err){ + retv.errno = err; + return retv; + } + + + if(GETTYPE(fd->mode) != TYPE_SOCKET){ + retv.errno = ENOTSOCK; + goto _ret; + } + + retv.errno = socket_bind(fd->node->objdata, addr, addrlen); + + _ret: + + fd_release(fd); + + return retv; + +} diff --git a/src/sys/syscalls/connect.c b/src/sys/syscalls/connect.c new file mode 100644 index 00000000..38b1d709 --- /dev/null +++ b/src/sys/syscalls/connect.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include + +syscallret syscall_connect(int sockfd, void* addr, size_t addrlen){ + + syscallret retv; + retv.ret = -1; + + if(addr > USER_SPACE_END){ + retv.errno = EFAULT; + return retv; + } + + proc_t* proc = arch_getcls()->thread->proc; + + fd_t* fd; + + int err = fd_access(&proc->fdtable, &fd, sockfd); + + if(err){ + retv.errno = err; + return retv; + } + + + if(GETTYPE(fd->mode) != TYPE_SOCKET){ + retv.errno = ENOTSOCK; + goto _ret; + } + + retv.errno = socket_connect(fd->node->objdata, addr, addrlen); + + _ret: + + fd_release(fd); + + return retv; + +} diff --git a/src/sys/syscalls/listen.c b/src/sys/syscalls/listen.c new file mode 100644 index 00000000..bbe8da6a --- /dev/null +++ b/src/sys/syscalls/listen.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include + +syscallret syscall_listen(int sockfd, int backlog){ + + syscallret retv; + retv.ret = -1; + + proc_t* proc = arch_getcls()->thread->proc; + + fd_t* fd; + + int err = fd_access(&proc->fdtable, &fd, sockfd); + + if(err){ + retv.errno = err; + return retv; + } + + + if(GETTYPE(fd->mode) != TYPE_SOCKET){ + retv.errno = ENOTSOCK; + goto _ret; + } + + retv.errno = socket_listen(fd->node->objdata, backlog); + + _ret: + + fd_release(fd); + + return retv; + +} diff --git a/src/sys/syscalls/open.c b/src/sys/syscalls/open.c index 5fc4e184..d9eb5001 100644 --- a/src/sys/syscalls/open.c +++ b/src/sys/syscalls/open.c @@ -95,6 +95,11 @@ syscallret syscall_openat(int dirfd, const char* pathname, int flags, mode_t mod goto _fail; } + if(GETTYPE(file->st.st_mode) == TYPE_SOCKET){ + retv.errno = ENXIO; + goto _fail; + } + fd->node = file; fd->mode = file->st.st_mode; diff --git a/src/sys/syscalls/socket_syscall.c b/src/sys/syscalls/socket_syscall.c new file mode 100644 index 00000000..4c96e017 --- /dev/null +++ b/src/sys/syscalls/socket_syscall.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +extern fs_t kerneltmpfs; + +syscallret syscall_socket(int domain, int type, int protocol){ + + syscallret retv; + retv.ret = -1; + + proc_t* proc = arch_getcls()->thread->proc; + + fd_t* fd = NULL; + int ifd; + + retv.errno = fd_alloc(&proc->fdtable, &fd, &ifd, 0); + + if(retv.errno) + return retv; + + fd->node = vfs_newnode("SOCKET", &kerneltmpfs, NULL); + if(!fd->node){ + retv.errno = ENOMEM; + goto _fail; + } + fd->mode = 0777 | MAKETYPE(TYPE_SOCKET); + fd->node->refcount = 1; + fd->node->st.st_mode = fd->mode; + + socket_t* sock; + + retv.errno = socket_new(&sock, domain, type, protocol); + + if(retv.errno) + goto _fail; + + fd->node->objdata = sock; + + fd_release(fd); + + retv.ret = ifd; + return retv; + + _fail: + + if(fd){ + fd_release(fd); + fd_free(&proc->fdtable, ifd); + } + + return retv; + +} diff --git a/src/sys/unsocket.c b/src/sys/unsocket.c new file mode 100644 index 00000000..9f855825 --- /dev/null +++ b/src/sys/unsocket.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int unsocket_new(socket_t** returnptr, int type, int protocol){ + + if(type != SOCK_STREAM) + return EINVAL; + + socket_t* socket = alloc(sizeof(socket_t)); + + if(!socket) + return ENOMEM; + + socket->family = AF_UNIX; + socket->type = type; + socket->protocol = protocol; + socket->state = SOCKET_STATE_UNBOUND; + if(ringbuffer_init(&socket->buffer, 1024*128)){ // 128kb for socket buff + free(socket); + return ENOMEM; + } + + *returnptr = socket; + + return 0; + +} + +int unsocket_bind(struct _socket_t* sock, sockaddr_un* addr, size_t addrlen){ + + // copy address + + if(addrlen > sizeof(sockaddr_un)) + return EINVAL; + + sockaddr_un unaddr; + + memcpy(&unaddr, addr, addrlen); + + if(unaddr.sun_family != AF_UNIX) + return EINVAL; + + // create and prepare the socket node + + spinlock_acquire(&sock->lock); + + proc_t* proc = arch_getcls()->thread->proc; + + int err = vfs_mksocket(unaddr.sun_path[0] == '/' ? proc->root : proc->cwd, unaddr.sun_path, 0777 & ~proc->umask); + + if(err) + goto _return; + + vnode_t* socketnode; + + err = vfs_open(&socketnode, unaddr.sun_path[0] == '/' ? proc->root : proc->cwd, unaddr.sun_path); + + if(err) + goto _return; + + socketnode->objdata = sock; + + sock->addr_un = unaddr; + sock->addrlen = addrlen; + sock->state = SOCKET_STATE_BOUND; + err = 0; + + _return: + + spinlock_release(&sock->lock); + + return err; + +} + +int unsocket_connect(struct _socket_t* sock, void* addr, size_t addrlen){ + + // copy address + + socket_t* peer = NULL; + + if(addrlen > sizeof(sockaddr_un)) + return EINVAL; + + sockaddr_un addr_un; + memcpy(&addr_un, addr, addrlen); + + if(addr_un.sun_family != AF_UNIX) + return EAFNOSUPPORT; + + // do a few checks on the socket to see if the request is valid + + proc_t* proc = arch_getcls()->thread->proc; + + int err = 0; + + spinlock_acquire(&sock->lock); + + if(sock->state == SOCKET_STATE_LISTENING){ + err = EINVAL; + goto _return; + } + + if(sock->state == SOCKET_STATE_CONNECTED){ + err = EISCONN; + goto _return; + } + + vnode_t* vnode = NULL; + + err = vfs_open(&vnode, addr_un.sun_path[0] == '/' ? proc->root : proc->cwd, addr_un.sun_path); + + if(err) + goto _return; + + if(GETTYPE(vnode->st.st_mode) != TYPE_SOCKET){ + err = ENOTSOCK; + goto _return; + } + + peer = vnode->objdata; + + spinlock_acquire(&peer->lock); + + if(peer->type != sock->type){ + err = EPROTOTYPE; + goto _return; + } + + if(peer->state != SOCKET_STATE_LISTENING){ + err = ECONNREFUSED; + goto _return; + } + + + + err = socket_addtobacklog(peer, sock); + if(err) + goto _return; + + arch_interrupt_disable(); + event_signal(&peer->connectevent, false); + + spinlock_release(&peer->lock); + spinlock_release(&sock->lock); + + err = event_wait(&sock->acceptevent, true); + + spinlock_acquire(&peer->lock); + spinlock_acquire(&sock->lock); + + // TODO remove from backlog! + if(err) + goto _return; + + + _return: + + if(peer) + spinlock_release(&peer->lock); + + if(vnode) + vfs_close(vnode); + + spinlock_release(&sock->lock); + + return err; + +} + +int unsocket_accept(socket_t** peer, socket_t* sock, void* addr, size_t* addrlen){ + + // TODO abort + + size_t len = *addrlen; + + int err = 0; + + if(*addrlen > sizeof(sockaddr_un)) + return EINVAL; + + + spinlock_acquire(&sock->lock); + + if(sock->state != SOCKET_STATE_LISTENING){ + err = EINVAL; + goto _return; + } + + // TODO interruptble + + while(!sock->backlogend){ + spinlock_release(&sock->lock); + event_wait(&sock->connectevent, true); + spinlock_acquire(&sock->lock); + } + + *peer = socket_popfrombacklog(sock); + + spinlock_release(&sock->lock); + + _return: + + return err; + +}