Add UDP broadcast and setsockopt()
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user