syscall_net: first pieces
authorAlan Cox <alan@linux.intel.com>
Mon, 1 Jun 2015 22:00:09 +0000 (23:00 +0100)
committerAlan Cox <alan@linux.intel.com>
Mon, 1 Jun 2015 22:00:09 +0000 (23:00 +0100)
Lots left to put together for the non protocol parts of the stack, but this
is a starting point.

Kernel/syscall_net.c [new file with mode: 0644]

diff --git a/Kernel/syscall_net.c b/Kernel/syscall_net.c
new file mode 100644 (file)
index 0000000..390ca66
--- /dev/null
@@ -0,0 +1,373 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <netdev.h>
+
+#define NSOCKET 8
+
+
+struct sockaddrs {
+       uint32_t laddr;
+       uint16_t lport;
+       uint32_t daddr;
+       uint16_t dport;
+};
+
+struct socket
+{
+       inoptr s_inode;
+       uint8_t s_type;
+       uint8_t s_error;
+       uint8_t s_state;
+#define SS_UNUSED              0
+#define SS_UNCONNECTED         1
+#define SS_BOUND               2
+#define SS_LISTENING           3
+#define SS_CONNECTING          4
+#define SS_CONNECTED           5
+       struct sockaddrs s_addr;
+};
+
+struct socket sockets[NSOCKET];
+uint32_t our_address;
+
+static int is_socket(inoptr ino)
+{
+       if ((ino->c_node.i_mode & F_MASK) == F_SOCK)
+               return 1;
+       return 0;
+}
+
+static int8_t alloc_socket(void)
+{
+       struct socket *s = sockets;
+       while (s < sockets + NSOCKET) {
+               if (s->s_state == SS_UNUSED)
+                       return s - sockets;
+               s++;
+       }
+       return -1;
+}
+
+static struct socket *sock_get(int fd, uint8_t *flag)
+{
+       struct oft *oftp;
+       inoptr ino = getinode(fd);
+       if (ino == NULLINODE)
+               return NULL;
+       if (!is_socket(ino)) {
+               udata.u_error = EINVAL;
+               return NULL;
+       }
+       if (flag) {
+               oftp = of_tab + udata.u_files[fd];
+               *flag = oftp->o_access;
+       }
+       return sockets + ino->c_node.i_nlink;
+}
+
+static int sock_pending(struct socket *s)
+{
+       /* TODO */
+       return 0;
+}
+
+static int sock_autobind(struct socket *s)
+{
+       /* TODO */
+       s->s_state = SS_BOUND;
+       return 0;
+}
+
+static struct socket *sock_find_local(uint32_t addr, uint16_t port)
+{
+       /* TODO */
+       return NULL;
+}
+
+static int sa_get(struct sockaddr_in *u, struct sockaddr_in *k)
+{
+       if (uget(u, k, sizeof(struct sockaddr_in)))
+               return -1;
+       if (k->sin_family != AF_INET) {
+               udata.u_error = EINVAL;
+               return -1;
+       }
+       return 0;
+}
+
+static int sa_getlocal(struct sockaddr_in *u, struct sockaddr_in *k)
+{
+       if (sa_get(u, k))
+               return -1;
+       if (ntohs(k->sin_port) < 1024 && !super()) {
+               udata.u_error = EACCES;
+               return -1;
+       }
+       if (k->sin_addr.s_addr != INADDR_ANY && 
+               !IN_LOOPBACK(k->sin_addr.s_addr) &&
+               k->sin_addr.s_addr != our_address) {
+               udata.u_error = EADDRNOTAVAIL;
+               return -1;
+       }
+       return 0;
+}
+
+static int sa_getremote(struct sockaddr_in *u, struct sockaddr_in *k)
+{
+       if (sa_get(u, k))
+               return -1;
+       if (ntohs(k->sin_port) >= 512 && ntohs(k->sin_port) < 1024 && !super()) {
+               udata.u_error = EACCES;
+               return -1;
+       }
+       return 0;
+}
+
+int sock_error(struct socket *s)
+{
+       udata.u_error = s->s_error;
+       s->s_error = 0;
+       if (udata.u_error)
+               return -1;
+       else
+               return 0;
+}
+
+struct sockinfo {
+       uint8_t af;
+       uint8_t pf;
+       uint8_t type;
+       uint8_t priv;
+};
+
+#define NSOCKTYPE 3
+struct sockinfo socktypes[NSOCKTYPE] = {
+       { AF_INET, SOCK_STREAM, IPPROTO_TCP, 0 },
+#define SOCKTYPE_TCP   1
+       { AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0 },
+#define SOCKTYPE_UDP   2
+       { AF_INET, SOCK_RAW, 0, 0 }
+#define SOCKTYPE_RAW   3
+};
+
+arg_t make_socket(struct sockinfo *s, int8_t *np)
+{
+       int8_t n;
+       int8_t uindex;
+       int8_t oftindex;
+       inoptr ino;
+
+       /* RAW sockets are superuser */
+       if (s->priv && esuper())
+               return -1;
+
+       /* Start by getting the file and inode table entries */
+       if ((uindex = uf_alloc()) == -1)
+               return -1;
+       if ((oftindex = oft_alloc()) == -1)
+               goto nooft;
+
+       if (np)
+               n = *np;
+       else {
+               n = alloc_socket();
+               if (n == -1)
+                       goto noalloc;
+       }
+       /* We need an inode : FIXME - do we want a pipedev aka Unix ? */
+       if (!(ino = i_open(root_dev, 0)))
+               goto noalloc;
+       /* All good - now set it up */
+       /* The nlink cheat needs to be taught to fsck! */
+       ino->c_node.i_mode = F_SOCK | 0777;
+       ino->c_node.i_nlink = n;        /* Cheat !! */
+
+       of_tab[oftindex].o_inode = ino;
+       of_tab[oftindex].o_access = O_RDWR;
+
+       udata.u_files[uindex] = oftindex;
+
+       sockets[n].s_inode = ino;
+       sockets[n].s_type = s - socktypes;      /* Pointer or uint8_t best ? */
+       sockets[n].s_state = SS_UNCONNECTED;
+
+       if (np)
+               *np = n;
+       return uindex;
+
+noalloc:
+       oft_deref(oftindex);    /* Will call i_deref! */
+nooft:
+       udata.u_files[uindex] = NO_FILE;
+       return -1;
+}
+
+/*******************************************
+socket (af, type, pf)           Function ??
+uint16_t af;
+uint16_t type;
+uint16_t pf;
+********************************************/
+
+#define afv   (uint16_t)udata.u_argn
+#define typev (uint16_t)udata.u_argn1
+#define pfv   (uint16_t)udata.u_argn2
+
+arg_t _socket(void)
+{
+       struct sockinfo *s = socktypes;
+
+       /* Find the socket */
+       while (s && s < socktypes + NSOCKTYPE) {
+               if (s->af == afv && s->type == typev) {
+                       if (s->pf == 0 || s->pf == pfv)
+                               return make_socket(s, NULL);
+               }
+               s++;
+       }
+       return -EAFNOSUPPORT;
+}
+
+#undef af
+#undef type
+#undef pf
+
+/*******************************************
+listen(fd, qlen)           Function ??
+int fd;
+int qlen;
+********************************************/
+#define fd   (int16_t)udata.u_argn
+#define qlen (int16_t)udata.u_argn1
+
+arg_t _listen(void)
+{
+       struct socket *s = sock_get(fd, NULL);
+       if (s == NULL)
+               return -1;
+       if (s->s_state == SS_UNCONNECTED && sock_autobind(s))
+               return -1;
+       if (s->s_type != SOCKTYPE_TCP || s->s_state != SS_BOUND) {
+               udata.u_error = EINVAL;
+               return -1;      
+       }
+       /* Call the protocol services */
+       s->s_state = SS_LISTENING;
+       return 0;
+}
+
+/*******************************************
+bind(fd, addr)           Function ??
+int fd;
+struct sockaddr_*in addr;
+********************************************/
+#define fd   (int16_t)udata.u_argn
+#define addr (struct sockaddr_in *)udata.u_argn1
+
+arg_t _bind(void)
+{
+       struct socket *s = sock_get(fd, NULL);
+       struct sockaddr_in sin;
+       if (s == NULL)
+               return -1;
+       if (s->s_state != SS_UNCONNECTED)
+               return -1;
+       if (sa_getlocal(addr, &sin) == -1)
+               return -1;
+       if (sock_find_local(sin.sin_addr.s_addr, sin.sin_port)) {
+               udata.u_error = EADDRINUSE;
+               return -1;
+       }
+       s->s_state = SS_BOUND;
+       return 0;
+}
+
+/*******************************************
+connect(fd, addr)           Function ??
+int fd;
+struct sockaddr_*in addr;
+********************************************/
+#define fd   (int16_t)udata.u_argn
+#define addr (struct sockaddr_in *)udata.u_argn1
+
+arg_t _connect(void)
+{
+       uint8_t flag;
+       struct socket *s = sock_get(fd, &flag);
+       struct sockaddr_in sin;
+       if (s == NULL)
+               return -1;
+       if (s->s_state == SS_CONNECTING) {
+               udata.u_error = EALREADY;
+               return -1;
+       }
+       if (s->s_state == SS_UNCONNECTED && sock_autobind(s))
+               return -1;
+       
+       if (s->s_state == SS_BOUND) {
+               if (sa_getremote(addr, &sin) == -1)
+                       return -1;
+               s->s_state = SS_CONNECTING;
+               /* Protocol op to kick off */
+       }
+
+       do {
+               /* FIXME: return EINPROGRESS not EINTR for SS_CONNECTING */
+               if (psleep_flags(s, flag))
+                       return -1;
+               /* Protocol state check */
+       } while (s->s_state == SS_CONNECTING);
+       return sock_error(s);
+}
+
+/*******************************************
+accept(fd, addr)           Function ??
+int fd;
+struct sockaddr_*in addr;
+********************************************/
+#define fd   (int16_t)udata.u_argn
+
+/* Note: We don't do address return, the library can handle it */
+arg_t _accept(void)
+{
+       uint8_t flag;
+       struct socket *s = sock_get(fd, &flag);
+       int8_t n;
+       int8_t nfd;
+
+       if (s == NULL)
+               return -1;
+       if (s->s_state == SS_LISTENING) {
+               udata.u_error = EALREADY;
+               return -1;
+       }
+       
+       while ((n = sock_pending(s)) != -1) {
+               if (psleep_flags(s, flag))
+                       return -1;
+               if (s->s_error)
+                       return sock_error(s);
+       }
+       if ((nfd = make_socket(&socktypes[SOCKTYPE_TCP], &n)) == -1)
+               return -1;
+       sockets[n].s_state = SS_CONNECTED;
+       return nfd;
+}
+
+/*******************************************
+getsockaddrs(fd, addr)           Function ??
+int fd;
+struct sockaddr_*in addr;
+********************************************/
+#define fd   (int16_t)udata.u_argn
+#define addrs (uint8_t *)udata.u_argn1
+
+arg_t _getsockaddrs(void)
+{
+       struct socket *s = sock_get(fd, NULL);
+
+       if (s == NULL)
+               return -1;
+       return uput(&s->s_addr, addrs, sizeof(struct sockaddrs));
+}
+