Add (sketchy) unix domain sockets

This commit is contained in:
Mark
2020-07-07 15:42:51 +03:00
parent ecf4262646
commit a6ccddd695
16 changed files with 528 additions and 30 deletions
+8 -6
View File
@@ -89,12 +89,14 @@ void *syscall_table[256] = {
[SYSCALL_NRX_MODULE_UNLOAD] = sys_module_unload,
// Network
// [SYSCALL_NR_SOCKET] = sys_socket,
// [SYSCALL_NR_SENDTO] = sys_sendto,
// [SYSCALL_NR_RECVFROM] = sys_recvfrom,
// [SYSCALL_NR_BIND] = sys_bind,
// [SYSCALL_NR_SETSOCKOPT] = sys_setsockopt,
// [SYSCALL_NRX_NETCTL] = sys_netctl,
[SYSCALL_NR_SOCKET] = sys_socket,
[SYSCALL_NR_CONNECT] = sys_connect,
[SYSCALL_NR_ACCEPT] = sys_accept,
[SYSCALL_NR_SENDTO] = sys_sendto,
[SYSCALL_NR_RECVFROM] = sys_recvfrom,
[SYSCALL_NR_BIND] = sys_bind,
[SYSCALL_NR_SETSOCKOPT] = sys_setsockopt,
[SYSCALL_NRX_NETCTL] = sys_netctl,
// Extension
[SYSCALL_NRX_TRACE] = sys_debug_trace,
+3 -2
View File
@@ -112,8 +112,9 @@ OBJS+=$(O)/net/net.o \
$(O)/net/if.o \
$(O)/net/socket.o \
$(O)/net/util.o \
$(O)/net/ports.o
#$(O)/sys/sys_net.o
$(O)/net/ports.o \
$(O)/net/unix.o \
$(O)/sys/sys_net.o
DIRS+=$(O)/net
+1 -1
View File
@@ -29,7 +29,7 @@ void ofile_close(struct vfs_ioctx *ioctx, struct ofile *of) {
--of->refcount;
if (of->refcount == 0) {
if (of->flags & OF_SOCKET) {
//net_close(&thr->ioctx, thr->fds[fd]);
net_close(ioctx, of);
} else {
vfs_close(ioctx, of);
}
+3
View File
@@ -388,6 +388,9 @@ int vfs_mknod(struct vfs_ioctx *ctx, const char *path, mode_t mode, struct vnode
case S_IFIFO:
type = VN_FIFO;
break;
case S_IFSOCK:
type = VN_SOCK;
break;
default:
panic("mknod: unsupported node type\n");
}
+1
View File
@@ -32,6 +32,7 @@ enum vnode_type {
VN_CHR,
VN_LNK,
VN_FIFO,
VN_SOCK,
VN_UNK,
VN_MNT,
};
+2
View File
@@ -13,7 +13,9 @@ struct sockops {
void (*close) (struct socket *);
int (*bind) (struct socket *, struct sockaddr *, size_t);
int (*connect) (struct socket *, struct sockaddr *, size_t);
int (*setsockopt) (struct socket *, int, void *, size_t);
int (*accept) (struct socket *, struct socket *);
int (*count_pending) (struct socket *);
};
+6
View File
@@ -27,7 +27,13 @@ ssize_t net_recvfrom(struct vfs_ioctx *ioctx,
size_t len,
struct sockaddr *sa,
size_t *salen);
int net_accept(struct vfs_ioctx *ioctx,
struct ofile *fd,
struct ofile **fd2,
struct sockaddr *sa,
size_t *salen);
int net_bind(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len);
int net_connect(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len);
int net_setsockopt(struct vfs_ioctx *ioctx, struct ofile *fd, int optname, void *optval, size_t optlen);
void net_close(struct vfs_ioctx *ioctx, struct ofile *fd);
+2
View File
@@ -8,4 +8,6 @@ int sys_socket(int domain, int type, int protocol);
ssize_t sys_sendto(int fd, const void *buf, size_t len, struct sockaddr *sa, size_t salen);
ssize_t sys_recvfrom(int fd, void *buf, size_t len, struct sockaddr *sa, size_t *salen);
int sys_bind(int fd, struct sockaddr *sa, size_t len);
int sys_connect(int fd, struct sockaddr *sa, size_t len);
int sys_setsockopt(int fd, int level, int optname, void *optval, size_t len);
int sys_accept(int fd, struct sockaddr *sa, size_t *salen);
+4
View File
@@ -38,6 +38,10 @@
#define ENOSYS 38
#define ELOOP 40
#define EADDRINUSE 98
#define ECONNRESET 104
#define ECONNREFUSED 111
#if defined(__KERNEL__)
const char *kstrerror(int e);
#endif
+4
View File
@@ -2,6 +2,10 @@
#include "sys/types.h"
/* Protocol families */
#define PF_LOCAL 1
#define PF_UNIX PF_LOCAL
#define AF_UNIX PF_UNIX
#define PF_INET 2
#define AF_INET PF_INET
+2
View File
@@ -53,6 +53,8 @@
#define SYSCALL_NRX_WAITPID 247
#define SYSCALL_NR_SOCKET 41
#define SYSCALL_NR_CONNECT 42
#define SYSCALL_NR_ACCEPT 43
#define SYSCALL_NR_SENDTO 44
#define SYSCALL_NR_RECVFROM 45
#define SYSCALL_NR_BIND 49
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#define UNIX_PATH_MAX 108
struct sockaddr_un {
uint16_t sun_family;
char sun_path[UNIX_PATH_MAX];
} __attribute__((packed));
+1
View File
@@ -21,6 +21,7 @@ KERNEL_HEADERS="include/user/fcntl.h \
include/user/netctl.h \
include/user/socket.h \
include/user/inet.h \
include/user/un.h \
include/user/mman.h \
include/user/video.h \
include/user/select.h \
+37
View File
@@ -97,6 +97,43 @@ ssize_t net_recvfrom(struct vfs_ioctx *ioctx,
}
}
int net_connect(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len) {
_assert(fd);
_assert(fd->flags & OF_SOCKET);
_assert(fd->socket.ioctx == ioctx);
_assert(fd->socket.op);
if (fd->socket.op->connect) {
return fd->socket.op->connect(&fd->socket, sa, len);
} else {
return -EINVAL;
}
}
int net_accept(struct vfs_ioctx *ioctx, struct ofile *fd, struct ofile **res, struct sockaddr *sa, size_t *len) {
_assert(fd);
_assert(fd->flags & OF_SOCKET);
_assert(fd->socket.ioctx == ioctx);
_assert(fd->socket.op);
int ret;
if (fd->socket.op->accept) {
struct ofile *ofile = ofile_create();
ofile->flags |= OF_SOCKET;
if ((ret = fd->socket.op->accept(&fd->socket, &ofile->socket)) != 0) {
return ret;
}
*res = ofile;
return 0;
} else {
return -EINVAL;
}
}
int net_bind(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len) {
_assert(fd);
_assert(fd->flags & OF_SOCKET);
+372
View File
@@ -0,0 +1,372 @@
#include "arch/amd64/cpu.h"
#include "sys/char/ring.h"
#include "user/socket.h"
#include "user/errno.h"
#include "sys/assert.h"
#include "sys/thread.h"
#include "net/socket.h"
#include "sys/string.h"
#include "sys/debug.h"
#include "user/stat.h"
#include "net/class.h"
#include "sys/heap.h"
#include "sys/attr.h"
#include "user/un.h"
#include "fs/node.h"
#include "fs/vfs.h"
// TODO: check for possible races
// TODO: SOCK_DGRAM
static int unix_class_supports(int proto) {
return proto == 0;
}
static int unix_socket_open(struct socket *sock);
static void unix_socket_close(struct socket *sock);
static int unix_socket_bind(struct socket *sock, struct sockaddr *sa, size_t len);
static int unix_socket_connect(struct socket *sock, struct sockaddr *sa, size_t len);
static int unix_socket_accept(struct socket *serv, struct socket *client);
static ssize_t unix_socket_sendto(struct socket *s,
const void *buf, size_t lim,
struct sockaddr *dst, size_t salen);
static ssize_t unix_socket_recvfrom(struct socket *s,
void *buf, size_t lim,
struct sockaddr *dst, size_t *salen);
static struct sockops unix_socket_ops = {
.open = unix_socket_open,
.close = unix_socket_close,
.sendto = unix_socket_sendto,
.recvfrom = unix_socket_recvfrom,
.bind = unix_socket_bind,
.connect = unix_socket_connect,
.accept = unix_socket_accept,
};
static struct socket_class unix_socket_class = {
.name = "unix",
.ops = &unix_socket_ops,
.domain = AF_UNIX,
.type = SOCK_STREAM,
.supports = unix_class_supports
};
static ssize_t unix_conn_sendto(struct socket *s,
const void *buf, size_t lim,
struct sockaddr *dst, size_t salen);
static ssize_t unix_conn_recvfrom(struct socket *s,
void *buf, size_t lim,
struct sockaddr *dst, size_t *salen);
static void unix_conn_close(struct socket *s);
static struct sockops unix_conn_ops = {
.sendto = unix_conn_sendto,
.recvfrom = unix_conn_recvfrom,
.close = unix_conn_close
};
////
enum unix_conn_state {
STATE_NEW = 1,
STATE_PRE_ESTABLISHED,
STATE_ESTABLISHED,
STATE_TERMINATED
};
struct unix_socket {
int type;
// Shared vnode
struct vnode *vnode;
// If server:
// receive notify when client attempts connections
// If client:
// receive notify when server acknowledges connection
struct io_notify client_notify;
// If server
size_t remote_count;
// If server:
// attempt of remote connection
// If client:
// remote side
struct unix_conn *remote;
};
struct unix_conn {
struct unix_socket *server;
struct unix_socket *client;
int state;
struct ring client_tx;
struct ring server_tx;
};
static int unix_socket_open(struct socket *sock) {
struct unix_socket *data = kmalloc(sizeof(struct unix_socket));
_assert(data);
sock->data = data;
data->type = 0;
data->remote = NULL;
data->remote_count = 0;
thread_wait_io_init(&data->client_notify);
return 0;
}
static void unix_socket_close(struct socket *sock) {
struct unix_socket *data = sock->data;
_assert(data);
if (data->type == 1) {
kdebug("Closing with remote_count = %d\n", data->remote_count);
_assert(data->remote_count == 0);
struct vnode *vn = data->vnode;
_assert(vn);
vn->fs_data = NULL;
} else {
// Hangup connection
if (data->remote) {
struct unix_conn *conn = data->remote;
// That's us
_assert(conn->client);
if (conn->state == STATE_ESTABLISHED) {
conn->state = STATE_TERMINATED;
// Send EOF to remote
_assert(conn->server);
ring_signal(&conn->client_tx, RING_SIGNAL_EOF);
}
conn->client = NULL;
if (!conn->server) {
kinfo("Client side removes the connection\n");
memset(conn, 0, sizeof(struct unix_conn));
kfree(conn);
}
}
}
kfree(data);
}
static void unix_conn_close(struct socket *s) {
struct unix_conn *conn = s->data;
_assert(conn);
// That's us
_assert(conn->server);
if (conn->state == STATE_ESTABLISHED) {
conn->state = STATE_TERMINATED;
// Send EOF to remote
_assert(conn->client);
ring_signal(&conn->server_tx, RING_SIGNAL_EOF);
conn->server = NULL;
}
if (!conn->client) {
_assert(conn->server->remote_count > 0);
--conn->server->remote_count;
kinfo("Server side removes the connection\n");
memset(conn, 0, sizeof(struct unix_conn));
kfree(conn);
}
}
static int unix_socket_bind(struct socket *sock, struct sockaddr *sa, size_t len) {
if (sa->sa_family != AF_UNIX) {
return -EINVAL;
}
struct sockaddr_un *sun = (struct sockaddr_un *) sa;
int res;
struct unix_socket *data = sock->data;
_assert(data);
// If socket already exists
if ((res = vfs_find(&thread_self->proc->ioctx, NULL, sun->sun_path, 0, &data->vnode)) == 0) {
data->vnode = NULL;
return -EADDRINUSE;
}
// Create socket vnode
if ((res = vfs_mknod(&thread_self->proc->ioctx, sun->sun_path, 0777 | S_IFSOCK, &data->vnode)) != 0) {
return res;
}
// Setup server socket params
data->type = 1;
data->vnode->fs_data = data;
return 0;
}
static int unix_socket_connect(struct socket *sock, struct sockaddr *sa, size_t len) {
if (sa->sa_family != AF_UNIX) {
return -EINVAL;
}
struct sockaddr_un *sun = (struct sockaddr_un *) sa;
struct unix_socket *data = sock->data;
int res;
_assert(data);
if ((res = vfs_find(&thread_self->proc->ioctx, NULL, sun->sun_path, 0, &data->vnode)) != 0) {
return -ENOENT;
}
if (!data->vnode->fs_data) {
return -ECONNREFUSED;
}
struct unix_conn *conn = kmalloc(sizeof(struct unix_conn));
conn->client = data;
conn->server = data->vnode->fs_data;
conn->state = STATE_NEW;
ring_init(&conn->client_tx, 1024);
ring_init(&conn->server_tx, 1024);
// Notify server of connection
_assert(!conn->server->remote);
conn->server->remote = conn;
thread_notify_io(&conn->server->client_notify);
// Await server connection ack
while ((conn->state == STATE_NEW)) {
thread_wait_io(thread_self, &data->client_notify);
}
_assert(conn->state == STATE_PRE_ESTABLISHED);
data->remote = conn;
// Confirm we're ready
conn->state = STATE_ESTABLISHED;
thread_notify_io(&conn->server->client_notify);
return 0;
}
static int unix_socket_accept(struct socket *serv, struct socket *client) {
struct unix_socket *data_serv;
struct unix_conn *conn;
data_serv = serv->data;
_assert(data_serv);
// Wait for incoming connection attempts
_assert(!(data_serv->remote));
while (!(conn = data_serv->remote)) {
thread_wait_io(thread_self, &data_serv->client_notify);
}
data_serv->remote = NULL;
// Setup client socket
client->data = conn;
client->op = &unix_conn_ops;
client->ioctx = serv->ioctx;
++data_serv->remote_count;
// Acknowledge connection attempt
conn->state = STATE_PRE_ESTABLISHED;
thread_notify_io(&conn->client->client_notify);
// Acknowledge client ready to prevent server from terminating
// connection too early
while (conn->state == STATE_PRE_ESTABLISHED) {
thread_wait_io(thread_self, &data_serv->client_notify);
}
return 0;
}
static ssize_t unix_ring_recv(struct ring *r, void *buf, size_t lim) {
size_t rd = 0;
char c;
size_t rem = lim;
char *wr = buf;
while (rem) {
if (ring_getc(thread_self, r, &c, 0) < 0) {
break;
}
*wr++ = c;
++rd;
--rem;
if (!ring_readable(r) && (r->flags & RING_SIGNAL_RET)) {
r->flags &= ~RING_SIGNAL_RET;
return rd;
}
}
return rd;
}
static ssize_t unix_socket_sendto(struct socket *s,
const void *buf, size_t lim,
struct sockaddr *dst, size_t salen) {
struct unix_socket *data = s->data;
_assert(data);
_assert(data->type == 0);
struct unix_conn *conn = data->remote;
_assert(conn);
if (conn->state != STATE_ESTABLISHED) {
return -ECONNRESET;
}
ssize_t res = ring_write(thread_self, &conn->client_tx, buf, lim, 1);
ring_signal(&conn->client_tx, RING_SIGNAL_RET);
return res;
}
static ssize_t unix_socket_recvfrom(struct socket *s,
void *buf, size_t lim,
struct sockaddr *dst, size_t *salen) {
struct unix_socket *data = s->data;
_assert(data);
_assert(data->type == 0);
struct unix_conn *conn = data->remote;
_assert(conn);
if (conn->state != STATE_ESTABLISHED && !ring_readable(&conn->server_tx)) {
return -ECONNRESET;
}
return unix_ring_recv(&conn->server_tx, buf, lim);
}
static ssize_t unix_conn_sendto(struct socket *s,
const void *buf, size_t lim,
struct sockaddr *dst, size_t salen) {
struct unix_conn *conn = s->data;
_assert(conn);
if (conn->state != STATE_ESTABLISHED) {
return -ECONNRESET;
}
ssize_t res = ring_write(thread_self, &conn->server_tx, buf, lim, 1);
ring_signal(&conn->server_tx, RING_SIGNAL_RET);
return res;
}
static ssize_t unix_conn_recvfrom(struct socket *s,
void *buf, size_t lim,
struct sockaddr *dst, size_t *salen) {
struct unix_conn *conn = s->data;
_assert(conn);
if (conn->state != STATE_ESTABLISHED && !ring_readable(&conn->client_tx)) {
return -ECONNRESET;
}
return unix_ring_recv(&conn->client_tx, buf, lim);
}
////
static __init void unix_class_register(void) {
socket_class_register(&unix_socket_class);
}
+74 -21
View File
@@ -22,14 +22,13 @@ int sys_netctl(const char *name, uint32_t op, void *arg) {
}
int sys_socket(int domain, int type, int protocol) {
struct thread *thr = get_cpu()->thread;
_assert(thr);
struct process *proc = thread_self->proc;
int fd = -1;
int res;
// XXX: This should be atomic
for (int i = 0; i < THREAD_MAX_FDS; ++i) {
if (!thr->fds[i]) {
if (!proc->fds[i]) {
fd = i;
break;
}
@@ -40,25 +39,24 @@ int sys_socket(int domain, int type, int protocol) {
struct ofile *ofile = ofile_create();
if ((res = net_open(&thr->ioctx, ofile, domain, type, protocol)) != 0) {
if ((res = net_open(&proc->ioctx, ofile, domain, type, protocol)) != 0) {
ofile_destroy(ofile);
return res;
}
thr->fds[fd] = ofile;
proc->fds[fd] = ofile_dup(ofile);
return fd;
}
ssize_t sys_sendto(int fd, const void *buf, size_t len, struct sockaddr *sa, size_t salen) {
struct thread *thr = thread_self;
struct process *proc = thread_self->proc;
struct ofile *of;
_assert(thr);
if (fd < 0 || fd >= THREAD_MAX_FDS) {
return -EBADF;
}
if ((of = thr->fds[fd]) == NULL) {
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
@@ -67,19 +65,18 @@ ssize_t sys_sendto(int fd, const void *buf, size_t len, struct sockaddr *sa, siz
return -EINVAL;
}
return net_sendto(&thr->ioctx, of, buf, len, sa, salen);
return net_sendto(&proc->ioctx, of, buf, len, sa, salen);
}
ssize_t sys_recvfrom(int fd, void *buf, size_t len, struct sockaddr *sa, size_t *salen) {
struct thread *thr = thread_self;
struct process *proc = thread_self->proc;
struct ofile *of;
_assert(thr);
if (fd < 0 || fd >= THREAD_MAX_FDS) {
return -EBADF;
}
if ((of = thr->fds[fd]) == NULL) {
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
@@ -88,19 +85,18 @@ ssize_t sys_recvfrom(int fd, void *buf, size_t len, struct sockaddr *sa, size_t
return -EINVAL;
}
return net_recvfrom(&thr->ioctx, of, buf, len, sa, salen);
return net_recvfrom(&proc->ioctx, of, buf, len, sa, salen);
}
int sys_bind(int fd, struct sockaddr *sa, size_t salen) {
struct thread *thr = thread_self;
struct process *proc = thread_self->proc;
struct ofile *of;
_assert(thr);
if (fd < 0 || fd >= THREAD_MAX_FDS) {
return -EBADF;
}
if ((of = thr->fds[fd]) == NULL) {
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
@@ -109,13 +105,70 @@ int sys_bind(int fd, struct sockaddr *sa, size_t salen) {
return -EINVAL;
}
return net_bind(&thr->ioctx, of, sa, salen);
return net_bind(&proc->ioctx, of, sa, salen);
}
int sys_connect(int fd, struct sockaddr *sa, size_t salen) {
struct process *proc = thread_self->proc;
struct ofile *of;
if (fd < 0 || fd >= THREAD_MAX_FDS) {
return -EBADF;
}
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
if (!(of->flags & OF_SOCKET)) {
kwarn("Invalid operation on non-socket\n");
return -EINVAL;
}
return net_connect(&proc->ioctx, of, sa, salen);
}
int sys_accept(int fd, struct sockaddr *sa, size_t *salen) {
struct process *proc = thread_self->proc;
struct ofile *of;
int res, client_fd = -1;
if (fd < 0 || fd >= THREAD_MAX_FDS) {
return -EBADF;
}
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
if (!(of->flags & OF_SOCKET)) {
kwarn("Invalid operation on non-socket\n");
return -EINVAL;
}
struct ofile *client_ofile = NULL;
if ((res = net_accept(&proc->ioctx, of, &client_ofile, sa, salen)) != 0) {
return res;
}
_assert(client_ofile);
// XXX: This should be atomic
for (int i = 0; i < THREAD_MAX_FDS; ++i) {
if (!proc->fds[i]) {
client_fd = i;
break;
}
}
if (client_fd == -1) {
return -EMFILE;
}
proc->fds[client_fd] = ofile_dup(client_ofile);
return client_fd;
}
int sys_setsockopt(int fd, int level, int optname, void *optval, size_t optlen) {
struct thread *thr = thread_self;
struct process *proc = thread_self->proc;
struct ofile *of;
_assert(thr);
// XXX: level is ignored (only 1 is used)
@@ -123,7 +176,7 @@ int sys_setsockopt(int fd, int level, int optname, void *optval, size_t optlen)
return -EBADF;
}
if ((of = thr->fds[fd]) == NULL) {
if ((of = proc->fds[fd]) == NULL) {
return -EBADF;
}
@@ -132,5 +185,5 @@ int sys_setsockopt(int fd, int level, int optname, void *optval, size_t optlen)
return -EINVAL;
}
return net_setsockopt(&thr->ioctx, of, optname, optval, optlen);
return net_setsockopt(&proc->ioctx, of, optname, optval, optlen);
}