Add UDP broadcast and setsockopt()

This commit is contained in:
Mark
2020-03-20 18:51:22 +02:00
parent f01a5c79ed
commit 22b201c6c0
13 changed files with 284 additions and 22 deletions
+3
View File
@@ -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,
};
+1
View File
@@ -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);
+8
View File
@@ -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);
+3
View File
@@ -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);
+3
View File
@@ -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);
+4
View File
@@ -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;
+3
View File
@@ -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;
+3
View File
@@ -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
+31 -5
View File
@@ -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) {
+44
View File
@@ -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;
}
}
+114 -16
View File
@@ -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);
}
}
}
}
+2 -1
View File
@@ -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;
+65
View File
@@ -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);
}