sockets: initial impl

This commit is contained in:
Matheus
2022-10-18 00:01:06 -03:00
parent e0953cd1b0
commit 4522b6c2da
14 changed files with 745 additions and 5 deletions
+85
View File
@@ -0,0 +1,85 @@
#include <kernel/socket.h>
#include <kernel/unsocket.h>
#include <errno.h>
#include <kernel/alloc.h>
#include <arch/spinlock.h>
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;
}
}
+105
View File
@@ -0,0 +1,105 @@
#include <kernel/syscalls.h>
#include <kernel/socket.h>
#include <kernel/fd.h>
#include <arch/cls.h>
#include <arch/spinlock.h>
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;
}
+44
View File
@@ -0,0 +1,44 @@
#include <kernel/syscalls.h>
#include <kernel/fd.h>
#include <arch/cls.h>
#include <errno.h>
#include <kernel/vmm.h>
#include <sys/stat.h>
#include <kernel/socket.h>
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;
}
+44
View File
@@ -0,0 +1,44 @@
#include <kernel/syscalls.h>
#include <kernel/fd.h>
#include <arch/cls.h>
#include <errno.h>
#include <kernel/vmm.h>
#include <sys/stat.h>
#include <kernel/socket.h>
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;
}
+38
View File
@@ -0,0 +1,38 @@
#include <kernel/syscalls.h>
#include <kernel/fd.h>
#include <arch/cls.h>
#include <errno.h>
#include <sys/stat.h>
#include <kernel/socket.h>
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;
}
+5
View File
@@ -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;
+55
View File
@@ -0,0 +1,55 @@
#include <kernel/syscalls.h>
#include <kernel/socket.h>
#include <kernel/fd.h>
#include <arch/cls.h>
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;
}
+213
View File
@@ -0,0 +1,213 @@
#include <kernel/socket.h>
#include <kernel/unsocket.h>
#include <kernel/alloc.h>
#include <arch/cls.h>
#include <errno.h>
#include <arch/spinlock.h>
#include <string.h>
#include <kernel/vfs.h>
#include <arch/interrupt.h>
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;
}