From 22b201c6c00beec55705a76a0fa8b3868d495a43 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 20 Mar 2020 18:51:22 +0200 Subject: [PATCH] Add UDP broadcast and setsockopt() --- arch/amd64/syscall.c | 3 + include/net/inet.h | 1 + include/net/net.h | 8 +++ include/net/udp.h | 3 + include/sys/sys_net.h | 3 + include/user/inet.h | 4 ++ include/user/socket.h | 3 + include/user/syscall.h | 3 + net/inet.c | 36 ++++++++++-- net/net.c | 44 ++++++++++++++ net/udp.c | 130 ++++++++++++++++++++++++++++++++++++----- sys/sched.c | 3 +- sys/sys_net.c | 65 +++++++++++++++++++++ 13 files changed, 284 insertions(+), 22 deletions(-) diff --git a/arch/amd64/syscall.c b/arch/amd64/syscall.c index e6c232b..5666861 100644 --- a/arch/amd64/syscall.c +++ b/arch/amd64/syscall.c @@ -66,6 +66,9 @@ void *syscall_table[256] = { // 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, }; diff --git a/include/net/inet.h b/include/net/inet.h index 43d6155..61028db 100644 --- a/include/net/inet.h +++ b/include/net/inet.h @@ -23,5 +23,6 @@ struct packet; struct eth_frame; void inet_handle_frame(struct packet *p, struct eth_frame *eth, void *data, size_t len); int inet_send_wrapped(struct netdev *src, uint32_t inaddr, uint8_t proto, void *data, size_t len); +int inet_send(uint32_t inaddr, uint8_t proto, void *data, size_t len); uint16_t inet_checksum(const void *data, size_t len); diff --git a/include/net/net.h b/include/net/net.h index edaa18c..0cc4c2d 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -14,5 +14,13 @@ ssize_t net_sendto(struct vfs_ioctx *ioctx, size_t len, struct sockaddr *sa, size_t salen); +ssize_t net_recvfrom(struct vfs_ioctx *ioctx, + struct ofile *fd, + void *buf, + size_t len, + struct sockaddr *sa, + size_t *salen); +int net_bind(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_daemon_start(void); diff --git a/include/net/udp.h b/include/net/udp.h index 6c7301b..df5031d 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -18,3 +18,6 @@ struct sockaddr; void udp_handle_frame(struct packet *p, struct eth_frame *eth, struct inet_frame *ip, void *data, size_t len); int udp_socket_open(struct vfs_ioctx *ioctx, struct ofile *fd, int dom, int type, int proto); ssize_t udp_socket_send(struct vfs_ioctx *ioctx, struct ofile *fd, const void *buf, size_t lim, struct sockaddr *sa, size_t salen); +ssize_t udp_socket_recv(struct vfs_ioctx *ioctx, struct ofile *fd, void *buf, size_t lim, struct sockaddr *sa, size_t *salen); +int udp_socket_bind(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len); +int udp_setsockopt(struct vfs_ioctx *ioctx, struct ofile *fd, int optname, void *optval, size_t optlen); diff --git a/include/sys/sys_net.h b/include/sys/sys_net.h index 764738c..c9228e8 100644 --- a/include/sys/sys_net.h +++ b/include/sys/sys_net.h @@ -6,3 +6,6 @@ struct sockaddr; int sys_netctl(const char *name, uint32_t op, void *arg); 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_setsockopt(int fd, int level, int optname, void *optval, size_t len); diff --git a/include/user/inet.h b/include/user/inet.h index 278023f..e056368 100644 --- a/include/user/inet.h +++ b/include/user/inet.h @@ -1,6 +1,10 @@ #pragma once #include "sys/types.h" +#define INADDR_ANY 0 +#define INADDR_BROADCAST 0xFFFFFFFF + + struct sockaddr_in { uint16_t sin_family; uint16_t sin_port; diff --git a/include/user/socket.h b/include/user/socket.h index 59d6257..bf3d50e 100644 --- a/include/user/socket.h +++ b/include/user/socket.h @@ -9,6 +9,9 @@ #define SOCK_STREAM 1 #define SOCK_DGRAM 2 +/* sockopts */ +#define SO_BINDTODEVICE 25 + #define SA_MAX_SIZE 64 struct sockaddr { uint16_t sa_family; diff --git a/include/user/syscall.h b/include/user/syscall.h index ebf496c..0405c54 100644 --- a/include/user/syscall.h +++ b/include/user/syscall.h @@ -45,4 +45,7 @@ #define SYSCALL_NR_SOCKET 41 #define SYSCALL_NR_SENDTO 44 +#define SYSCALL_NR_RECVFROM 45 +#define SYSCALL_NR_BIND 49 +#define SYSCALL_NR_SETSOCKOPT 54 #define SYSCALL_NRX_NETCTL 248 diff --git a/net/inet.c b/net/inet.c index cfb871b..6b8b2d8 100644 --- a/net/inet.c +++ b/net/inet.c @@ -1,5 +1,6 @@ #include "net/packet.h" #include "user/errno.h" +#include "user/inet.h" #include "sys/debug.h" #include "sys/panic.h" #include "net/util.h" @@ -23,15 +24,23 @@ uint16_t inet_checksum(const void *data, size_t len) { } int inet_send_wrapped(struct netdev *src, uint32_t inaddr, uint8_t proto, void *data, size_t len) { - if (!(src->flags & IF_F_HASIP)) { - kwarn("%s: has no inaddr\n", src->name); - return -EINVAL; + uint32_t src_inaddr; + + if (inaddr == INADDR_BROADCAST) { + src_inaddr = 0; + } else { + if (!(src->flags & IF_F_HASIP)) { + kwarn("%s: has no inaddr\n", src->name); + return -EINVAL; + } + + src_inaddr = src->inaddr; } struct inet_frame *ip = data + sizeof(struct eth_frame); ip->dst_inaddr = htonl(inaddr); - ip->src_inaddr = htonl(src->inaddr); + ip->src_inaddr = htonl(src_inaddr); ip->checksum = 0; ip->tos = 0; ip->ttl = 64; @@ -43,7 +52,24 @@ int inet_send_wrapped(struct netdev *src, uint32_t inaddr, uint8_t proto, void * ip->checksum = inet_checksum(ip, sizeof(struct inet_frame)); - return arp_send(src, inaddr, ETH_T_IP, data, len); + if (inaddr == INADDR_BROADCAST) { + // No need for ARP when broadcasting + return eth_send_wrapped(src, ETH_A_BROADCAST, ETH_T_IP, data, len); + } else { + return arp_send(src, inaddr, ETH_T_IP, data, len); + } +} + +int inet_send(uint32_t inaddr, uint8_t proto, void *data, size_t len) { + // TODO: find out what to do when broadcasting here + struct netdev *dev = netdev_find_inaddr(inaddr); + if (!dev) { + // TODO; ??? + kinfo("ENOENT\n"); + return -ENOENT; + } + + return inet_send_wrapped(dev, inaddr, proto, data, len); } void inet_handle_frame(struct packet *p, struct eth_frame *eth, void *data, size_t len) { diff --git a/net/net.c b/net/net.c index 354c460..806ff92 100644 --- a/net/net.c +++ b/net/net.c @@ -152,3 +152,47 @@ ssize_t net_sendto(struct vfs_ioctx *ioctx, return -EINVAL; } } + +ssize_t net_recvfrom(struct vfs_ioctx *ioctx, + struct ofile *fd, + void *buf, + size_t len, + struct sockaddr *sa, + size_t *salen) { + _assert(fd); + _assert(fd->flags & OF_SOCKET); + + switch (fd->socket.family) { + case SOCK_DGRAM: + return udp_socket_recv(ioctx, fd, buf, len, sa, salen); + default: + return -EINVAL; + } +} + +int net_bind(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len) { + _assert(fd); + + // TODO: match socket domain (AF_INET or not) + _assert(sa->sa_family == AF_INET); + + // CHANGE socket.family TO socket.type + + switch (fd->socket.family) { + case SOCK_DGRAM: + return udp_socket_bind(ioctx, fd, sa, len); + default: + return -EINVAL; + } +} + +int net_setsockopt(struct vfs_ioctx *ioctx, struct ofile *fd, int optname, void *optval, size_t optlen) { + _assert(fd); + + switch (fd->socket.family) { + case SOCK_DGRAM: + return udp_setsockopt(ioctx, fd, optname, optval, optlen); + default: + return -EINVAL; + } +} diff --git a/net/udp.c b/net/udp.c index 90c3b3d..5fa258a 100644 --- a/net/udp.c +++ b/net/udp.c @@ -20,17 +20,26 @@ #define UDP_SOCKET_ANY (1 << 0) // Accept port match (when calling recvfrom) #define UDP_SOCKET_APM (1 << 1) +// Socket is awaiting packets +#define UDP_SOCKET_PENDING (1 << 2) struct udp_socket { uint32_t flags; uint16_t port; // Current uint16_t recv_port; // recvfrom() port + uint32_t recv_inaddr; + struct netdev *bound; // Bound interface (all packets go to this interface if set) struct thread *owner; // Thread to suspend on blocking operation struct packet *pending; }; static uint16_t udp_eph_port = 32768; +// Only allowed for binding now +#define UDP_BIND_COUNT 16 +#define UDP_BIND_START 60 +static struct udp_socket *udp_ports[UDP_BIND_COUNT] = {0}; + struct udp_socket *udp_socket_create(void) { struct udp_socket *sock = kmalloc(sizeof(struct udp_socket)); _assert(sock); @@ -40,6 +49,7 @@ struct udp_socket *udp_socket_create(void) { sock->recv_port = 0; sock->owner = NULL; sock->pending = NULL; + sock->bound = NULL; return sock; } @@ -54,17 +64,41 @@ int udp_socket_open(struct vfs_ioctx *ioctx, struct ofile *fd, int dom, int type return 0; } -ssize_t udp_socket_recv_block(struct udp_socket *sock, void *buf, size_t lim) { +ssize_t udp_socket_recv(struct vfs_ioctx *ioctx, struct ofile *fd, void *buf, size_t lim, struct sockaddr *sa, size_t *salen) { + struct udp_socket *sock = fd->socket.sock; + _assert(sock); struct thread *t = thread_self; + struct packet *p; _assert(t); sock->owner = t; - while (!sock->pending) { - sched_unqueue(t, THREAD_WAITING_NET); - thread_check_signal(t, 0); + + if (!sock->pending) { + sock->flags |= UDP_SOCKET_PENDING; + + while ((sock->flags & UDP_SOCKET_PENDING)) { + sched_unqueue(t, THREAD_WAITING_NET); + thread_check_signal(t, 0); + } } - return -1; + // Read a single packet + p = sock->pending; + sock->pending = p->next; + + size_t f_size = sizeof(struct eth_frame) + sizeof(struct inet_frame) + sizeof(struct udp_frame); + const void *p_data = p->data + f_size; + size_t p_size = p->size - f_size; + + // TODO: track position in packet so it can be read partially + if (p_size > lim) { + panic("Not implemented: partial packet reading\n"); + } + + memcpy(buf, p_data, p_size); + packet_unref(p); + + return p_size; } ssize_t udp_socket_send(struct vfs_ioctx *ioctx, struct ofile *fd, const void *buf, size_t lim, struct sockaddr *sa, size_t salen) { @@ -97,21 +131,85 @@ ssize_t udp_socket_send(struct vfs_ioctx *ioctx, struct ofile *fd, const void *b memcpy(data, buf, lim); - struct netdev *dev = netdev_find_inaddr(ntohl(sin->sin_addr)); - if (!dev) { - // TODO; ??? - return -ENOENT; - } - - if ((res = inet_send_wrapped(dev, ntohl(sin->sin_addr), INET_P_UDP, packet, sizeof(packet))) != 0) { - return res; + if (sock->bound) { + if ((res = inet_send_wrapped(sock->bound, ntohl(sin->sin_addr), INET_P_UDP, packet, sizeof(packet))) != 0) { + return res; + } + } else { + if ((res = inet_send(ntohl(sin->sin_addr), INET_P_UDP, packet, sizeof(packet))) != 0) { + return res; + } } return lim; } -void udp_handle_frame(struct packet *p, struct eth_frame *eth, struct inet_frame *ip, void *data, size_t len) { - packet_ref(p); +int udp_socket_bind(struct vfs_ioctx *ioctx, struct ofile *fd, struct sockaddr *sa, size_t len) { + struct udp_socket *sock = fd->socket.sock; + struct sockaddr_in *sin; + _assert(sa); + _assert(sock); + _assert(sa->sa_family == AF_INET); + sin = (struct sockaddr_in *) sa; - packet_unref(p); + // TODO: allow binding sockets with ephemeral ports + _assert(!(sock->flags)); + + uint16_t port = ntohs(sin->sin_port); + if (port < UDP_BIND_START || (port - UDP_BIND_START) >= UDP_BIND_COUNT) { + return -EINVAL; + } + udp_ports[port] = sock; + + sock->flags |= UDP_SOCKET_ANY; + sock->recv_port = port; + sock->recv_inaddr = ntohl(sin->sin_addr); + + return 0; +} + +int udp_setsockopt(struct vfs_ioctx *ioctx, struct ofile *fd, int optname, void *optval, size_t optlen) { + struct udp_socket *sock = fd->socket.sock; + _assert(sock); + + switch (optname) { + case SO_BINDTODEVICE: + // TODO: safety? + if (!(sock->bound = netdev_by_name(optval))) { + kinfo("ENOENT\n"); + return -ENOENT; + } + return 0; + default: + return -EINVAL; + } +} + +void udp_handle_frame(struct packet *p, struct eth_frame *eth, struct inet_frame *ip, void *data, size_t len) { + struct udp_frame *udp; + + if (len < sizeof(struct udp_frame)) { + return; + } + + udp = data; + + uint16_t dpt = ntohs(udp->dst_port); + + if (dpt >= UDP_BIND_START && (dpt - UDP_BIND_START) < UDP_BIND_COUNT) { + // Check if it's a packet for one of "listening" sockets + struct udp_socket *sock = udp_ports[dpt - UDP_BIND_START]; + + if (sock) { + // Add "pending" packet for this socket + packet_ref(p); + p->next = sock->pending; + sock->pending = p; + + if (sock->flags & UDP_SOCKET_PENDING) { + sock->flags &= ~UDP_SOCKET_PENDING; + sched_queue(sock->owner); + } + } + } } diff --git a/sys/sched.c b/sys/sched.c index b52fae2..0201ad7 100644 --- a/sys/sched.c +++ b/sys/sched.c @@ -106,7 +106,8 @@ void sched_unqueue(struct thread *thr, enum thread_state new_state) { _assert((new_state == THREAD_WAITING) || (new_state == THREAD_STOPPED) || (new_state == THREAD_WAITING_IO) || - (new_state == THREAD_WAITING_PID)); + (new_state == THREAD_WAITING_PID) || + (new_state == THREAD_WAITING_NET)); _assert(queue_sizes[cpu_no]); --queue_sizes[cpu_no]; thr->state = new_state; diff --git a/sys/sys_net.c b/sys/sys_net.c index fac93b0..96e23ba 100644 --- a/sys/sys_net.c +++ b/sys/sys_net.c @@ -69,3 +69,68 @@ ssize_t sys_sendto(int fd, const void *buf, size_t len, struct sockaddr *sa, siz return net_sendto(&thr->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 ofile *of; + _assert(thr); + + if (fd < 0 || fd >= THREAD_MAX_FDS) { + return -EBADF; + } + + if ((of = thr->fds[fd]) == NULL) { + return -EBADF; + } + + if (!(of->flags & OF_SOCKET)) { + kwarn("Invalid operation on non-socket\n"); + return -EINVAL; + } + + return net_recvfrom(&thr->ioctx, of, buf, len, sa, salen); +} + +int sys_bind(int fd, struct sockaddr *sa, size_t salen) { + struct thread *thr = thread_self; + struct ofile *of; + _assert(thr); + + if (fd < 0 || fd >= THREAD_MAX_FDS) { + return -EBADF; + } + + if ((of = thr->fds[fd]) == NULL) { + return -EBADF; + } + + if (!(of->flags & OF_SOCKET)) { + kwarn("Invalid operation on non-socket\n"); + return -EINVAL; + } + + return net_bind(&thr->ioctx, of, sa, salen); +} + +int sys_setsockopt(int fd, int level, int optname, void *optval, size_t optlen) { + struct thread *thr = thread_self; + struct ofile *of; + _assert(thr); + + // XXX: level is ignored (only 1 is used) + + if (fd < 0 || fd >= THREAD_MAX_FDS) { + return -EBADF; + } + + if ((of = thr->fds[fd]) == NULL) { + return -EBADF; + } + + if (!(of->flags & OF_SOCKET)) { + kwarn("Invalid operation on non-socket\n"); + return -EINVAL; + } + + return net_setsockopt(&thr->ioctx, of, optname, optval, optlen); +}