diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b9c27ede..14863215 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -164,6 +164,7 @@ SET(KERNEL_SRCS syscall/read_write.cpp syscall/sigaction.cpp syscall/sleep.cpp + syscall/socket.cpp syscall/stat.cpp syscall/thread.cpp syscall/truncate.cpp @@ -174,7 +175,10 @@ SET(KERNEL_SRCS StackWalker.cpp net/NetworkAdapter.cpp net/E1000Adapter.cpp - net/NetworkManager.cpp) + net/NetworkManager.cpp + net/Socket.cpp + net/IPSocket.cpp + net/UDPSocket.cpp) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/duckos_version.h" diff --git a/kernel/api/in.h b/kernel/api/in.h new file mode 100644 index 00000000..e86aae29 --- /dev/null +++ b/kernel/api/in.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "socket.h" + +__DECL_BEGIN + +typedef uint16_t in_port_t; +typedef uint32_t in_addr_t; + +struct in_addr { + in_addr_t s_addr; +}; + +struct sockaddr_in { + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; +}; + +# define INADDR_ANY ((uint32_t) 0x00000000) +# define INADDR_NONE 0xffffffff +# define INPORT_ANY 0 +#define IPPROTO_TCP 0 +#define IPPROTO_UDP 0 + +__DECL_END \ No newline at end of file diff --git a/kernel/api/ipv4.h b/kernel/api/ipv4.h index 3651853c..b7e3e7ab 100644 --- a/kernel/api/ipv4.h +++ b/kernel/api/ipv4.h @@ -11,6 +11,13 @@ class __attribute__((packed)) IPv4Address { public: constexpr IPv4Address() = default; + constexpr IPv4Address(uint32_t addr) { + m_data[0] = addr >> 24; + m_data[1] = addr >> 16; + m_data[2] = addr >> 8; + m_data[3] = addr; + } + constexpr IPv4Address(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { m_data[0] = a; m_data[1] = b; diff --git a/kernel/api/socket.h b/kernel/api/socket.h new file mode 100644 index 00000000..18c26017 --- /dev/null +++ b/kernel/api/socket.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "cdefs.h" +#include "types.h" +#include "un.h" + +__DECL_BEGIN + +// Domains +#define AF_UNSPEC 0 +#define AF_UNIX 1 +#define AF_LOCAL 2 +#define AF_INET 3 +#define AF_PACKET 4 + +// Types +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +typedef uint16_t sa_family_t; +typedef uint32_t socklen_t; + +struct sockaddr { + sa_family_t sa_family; + char sa_data[14]; +}; + +struct iovec { + void* iov_base; + size_t iov_len; +}; + +struct msghdr { + void* msg_name; + socklen_t msg_namelen; + struct iovec* msg_iov; + size_t msg_iovlen; + void* msg_control; + size_t msg_controllen; + int msg_flags; +}; + +struct sockaddr_storage { + sa_family_t ss_family; + struct sockaddr_un __un; +}; + +__DECL_END \ No newline at end of file diff --git a/kernel/api/udp.h b/kernel/api/udp.h new file mode 100644 index 00000000..d0f819ea --- /dev/null +++ b/kernel/api/udp.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "endian.h" + +struct UDPPacket { + BigEndian source_port; + BigEndian dest_port; + BigEndian len; + BigEndian checksum = 0; + uint8_t payload[]; +} __attribute__((packed)); + +static_assert(sizeof(UDPPacket) == 8); \ No newline at end of file diff --git a/kernel/api/un.h b/kernel/api/un.h new file mode 100644 index 00000000..ef1fa61f --- /dev/null +++ b/kernel/api/un.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "types.h" + +__DECL_BEGIN + +#define UNIX_PATH_MAX 100 + +struct sockaddr_un { + uint16_t sun_family; + char sun_path[UNIX_PATH_MAX]; +}; + +__DECL_END \ No newline at end of file diff --git a/kernel/filesystem/File.cpp b/kernel/filesystem/File.cpp index b65d2109..c2481432 100644 --- a/kernel/filesystem/File.cpp +++ b/kernel/filesystem/File.cpp @@ -52,6 +52,10 @@ bool File::is_fifo() { return false; } +bool File::is_socket() { + return false; +} + ssize_t File::read(FileDescriptor &fd, size_t offset, SafePointer buffer, size_t count) { return 0; } diff --git a/kernel/filesystem/File.h b/kernel/filesystem/File.h index a8c92ff9..dfe43124 100644 --- a/kernel/filesystem/File.h +++ b/kernel/filesystem/File.h @@ -37,6 +37,7 @@ class File { virtual bool is_pty_mux(); virtual bool is_pty(); virtual bool is_fifo(); + virtual bool is_socket(); virtual int ioctl(unsigned request, SafePointer argp); virtual void open(FileDescriptor& fd, int options); virtual void close(FileDescriptor& fd); diff --git a/kernel/kstd/ListQueue.h b/kernel/kstd/ListQueue.h new file mode 100644 index 00000000..20e5aa4b --- /dev/null +++ b/kernel/kstd/ListQueue.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "types.h" +#include "Optional.h" + +namespace kstd { + +template +class ListQueue { +public: + ListQueue() = default; + + bool enqueue(const T& val) { + if (m_count >= max_count) + return false; + + if (m_tail == nullptr) { + m_head = new Entry {nullptr, val}; + m_head = m_tail; + } else { + m_tail->next = new Entry {nullptr, val}; + m_tail = m_tail->next; + } + + m_count++; + + return true; + } + + Optional dequeue() { + if (!m_head) + return nullopt; + + m_count--; + auto* ent = m_head; + m_head = ent->next; + if (m_tail == ent) + m_tail = nullptr; + return ent->val; + } + + Optional peek() { + if (!m_head) + return nullopt; + return m_head->val; + } + + [[nodiscard]] bool empty() const { + return m_count == 0; + } + + [[nodiscard]] size_t count() const { + return count; + } + +private: + struct Entry { + Entry* next; + T val; + }; + + Entry* m_head = nullptr; + Entry* m_tail = nullptr; + size_t m_count = 0; +}; + +} diff --git a/kernel/memory/SafePointer.h b/kernel/memory/SafePointer.h index b5e68e5a..c610186c 100644 --- a/kernel/memory/SafePointer.h +++ b/kernel/memory/SafePointer.h @@ -9,6 +9,7 @@ template class SafePointer { public: + SafePointer() = default; explicit SafePointer(T* raw_ptr, bool is_user): m_ptr(raw_ptr), m_is_user(is_user) {} template SafePointer(const SafePointer& safe_ptr): @@ -153,9 +154,14 @@ class SafePointer { }); } + template + SafePointer as() { + return SafePointer((R*) m_ptr, m_is_user); + } + private: - T* const m_ptr; - const bool m_is_user; + T* const m_ptr = nullptr; + const bool m_is_user = false; }; template diff --git a/kernel/net/IPSocket.cpp b/kernel/net/IPSocket.cpp new file mode 100644 index 00000000..b62edea2 --- /dev/null +++ b/kernel/net/IPSocket.cpp @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "IPSocket.h" +#include "UDPSocket.h" +#include "../api/in.h" +#include "../kstd/KLog.h" +#include "../tasking/PollBlocker.h" +#include "../filesystem/FileDescriptor.h" + +IPSocket::IPSocket(Socket::Type type, int protocol): Socket(Domain::Inet, type, protocol) { + +} + +ResultRet> IPSocket::make(Socket::Type type, int protocol) { + switch (type) { + case Type::Dgram: + return kstd::static_pointer_cast(TRY(UDPSocket::make())); + default: + return Result(EINVAL); + } +} + +Result IPSocket::bind(SafePointer addr_ptr, socklen_t addrlen) { + if (m_bound || addrlen != sizeof(sockaddr_in)) + return Result(set_error(EINVAL)); + + auto addr = addr_ptr.as().get(); + if (addr.sin_family != AF_INET) + return Result(set_error(EINVAL)); + + m_port = from_big_endian(addr.sin_port); + m_addr = IPv4Address(from_big_endian(addr.sin_addr.s_addr)); + + return do_bind(); +} + +ssize_t IPSocket::recvfrom(FileDescriptor& fd, SafePointer buf, size_t len, int flags, SafePointer src_addr, SafePointer addrlen) { + m_receive_queue_lock.acquire(); + + // Block until we have a packet to read + while (m_receive_queue.empty()) { + if (fd.nonblock()) { + m_receive_queue_lock.release(); + return -EAGAIN; + } + + update_blocker(); + m_receive_queue_lock.release(); + TaskManager::current_thread()->block(m_receive_blocker); + m_receive_queue_lock.acquire(); + } + + // Read our packet + auto* packet = m_receive_queue.pop_front(); + update_blocker(); + m_receive_queue_lock.release(); + auto res = do_recv(packet, buf, len); + kfree(packet); + return res; +} + +Result IPSocket::recv_packet(const void* buf, size_t len) { + LOCK(m_receive_queue_lock); + + if (m_receive_queue.size() == m_receive_queue.capacity()) { + KLog::warn("IPSocket", "Dropping packet because receive queue is full"); + return Result(ENOSPC); + } + + auto* src_pkt = (const IPv4Packet*) buf; + auto* new_pkt = (IPv4Packet*) kmalloc(len); + memcpy(new_pkt, src_pkt, len); + + m_receive_queue.push_back(new_pkt); + update_blocker(); + + return Result(SUCCESS); +} + +bool IPSocket::can_read(const FileDescriptor& fd) { + return !m_receive_queue.empty(); +} + +void IPSocket::update_blocker() { + m_receive_blocker.set_ready(!m_receive_queue.empty()); +} diff --git a/kernel/net/IPSocket.h b/kernel/net/IPSocket.h new file mode 100644 index 00000000..a2fa2bcc --- /dev/null +++ b/kernel/net/IPSocket.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "Socket.h" +#include "../api/ipv4.h" +#include "../kstd/ListQueue.h" + +class IPSocket: public Socket { +public: + static ResultRet> make(Socket::Type type, int protocol); + + // Socket + Result bind(SafePointer addr, socklen_t addrlen) override; + ssize_t recvfrom(FileDescriptor &fd, SafePointer buf, size_t len, int flags, SafePointer src_addr, SafePointer addrlen) override; + Result recv_packet(const void* buf, size_t len) override; + + // File + bool can_read(const FileDescriptor &fd) override; + +protected: + IPSocket(Socket::Type type, int protocol); + + virtual ssize_t do_recv(const IPv4Packet* pkt, SafePointer buf, size_t len) = 0; + virtual Result do_bind() = 0; + + void update_blocker(); + + bool m_bound = false; + uint16_t m_port; + IPv4Address m_addr; + kstd::circular_queue m_receive_queue { 16 }; + Mutex m_receive_queue_lock { "IPSocket::receive_queue" }; + BooleanBlocker m_receive_blocker; +}; diff --git a/kernel/net/NetworkManager.cpp b/kernel/net/NetworkManager.cpp index 807c48a6..dd58920c 100644 --- a/kernel/net/NetworkManager.cpp +++ b/kernel/net/NetworkManager.cpp @@ -4,6 +4,8 @@ #include "NetworkManager.h" #include "../kstd/KLog.h" #include "ICMP.h" +#include "UDPSocket.h" +#include "../api/udp.h" #define ARP_DEBUG 1 @@ -103,7 +105,11 @@ void NetworkManager::handle_ipv4(NetworkAdapter* adapter, const NetworkAdapter:: handle_icmp(adapter, packet); break; case IPv4Proto::TCP: + KLog::warn("NetworkManager", "Received TCP packet! Can't handle this yet!"); + break; case IPv4Proto::UDP: + handle_udp(adapter, packet); + break; default: KLog::warn("NetworkManager", "Received IPv4 packet with unknown protocol %d!", packet.proto); } @@ -116,3 +122,25 @@ void NetworkManager::handle_icmp(NetworkAdapter* adapter, const IPv4Packet& pack } const auto& header= *((ICMPHeader*) packet.payload); } + +void NetworkManager::handle_udp(NetworkAdapter* adapter, const IPv4Packet& packet) { + if (packet.length < (sizeof(IPv4Packet) + sizeof(UDPPacket))) { + KLog::warn("NetworkManager", "Received UDP packet of invalid size!"); + return; + } + + auto* udp_pkt = (UDPPacket*) packet.payload; + if (udp_pkt->len < sizeof(UDPPacket)) { + KLog::warn("NetworkManager", "Received UDP packet of invalid size!"); + return; + } + + // Get the socket associated with the port + auto sock = UDPSocket::get_socket(udp_pkt->dest_port); + if (!sock) { + KLog::warn("NetworkManager", "Received UDP packet for port %d but no such port is bound.", udp_pkt->dest_port.val()); + return; + } + + sock->recv_packet((uint8_t*) &packet, packet.length.val()); +} diff --git a/kernel/net/NetworkManager.h b/kernel/net/NetworkManager.h index a6f56031..e2d66f84 100644 --- a/kernel/net/NetworkManager.h +++ b/kernel/net/NetworkManager.h @@ -22,6 +22,7 @@ class NetworkManager { void handle_arp(NetworkAdapter* adapter, const NetworkAdapter::Packet* packet); void handle_ipv4(NetworkAdapter* adapter, const NetworkAdapter::Packet* packet); void handle_icmp(NetworkAdapter* adapter, const IPv4Packet& packet); + void handle_udp(NetworkAdapter* adapter, const IPv4Packet& packet); static NetworkManager* s_inst; diff --git a/kernel/net/Socket.cpp b/kernel/net/Socket.cpp new file mode 100644 index 00000000..d62b79f4 --- /dev/null +++ b/kernel/net/Socket.cpp @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "Socket.h" +#include "IPSocket.h" + +Socket::Socket(Socket::Domain domain, Socket::Type type, int protocol): + m_domain(domain), + m_type(type), + m_protocol(protocol) +{} + +ResultRet> Socket::make(Socket::Domain domain, Socket::Type type, int protocol) { + switch (domain) { + case Domain::Inet: + return kstd::static_pointer_cast(TRY(IPSocket::make(type, protocol))); + default: + return Result(EINVAL); + } +} + +ssize_t Socket::read(FileDescriptor& fd, size_t offset, SafePointer buffer, size_t count) { + return recvfrom(fd, buffer, count, 0, {}, {}); +} diff --git a/kernel/net/Socket.h b/kernel/net/Socket.h new file mode 100644 index 00000000..eea38b9f --- /dev/null +++ b/kernel/net/Socket.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "../filesystem/File.h" +#include "../api/socket.h" + +class Socket: public File { +public: + enum Domain { + Inet = AF_INET, + Local = AF_LOCAL, + Packet = AF_PACKET, + Unix = AF_UNIX + }; + + enum Type { + Stream = SOCK_STREAM, + Dgram = SOCK_DGRAM, + Raw = SOCK_RAW + }; + + static ResultRet> make(Domain domain, Type type, int protocol); + + // Socket + virtual Result bind(SafePointer addr, socklen_t addrlen) = 0; + virtual ssize_t recvfrom(FileDescriptor& fd, SafePointer buf, size_t len, int flags, SafePointer src_addr, SafePointer addrlen) = 0; + virtual Result recv_packet(const void* buf, size_t len) = 0; + + [[nodiscard]] int error() const { return m_error; } + + // File + bool is_socket() override { return true; } + ssize_t read(FileDescriptor &fd, size_t offset, SafePointer buffer, size_t count) override; + +protected: + Socket(Domain domain, Type type, int protocol); + + int set_error(int error) { m_error = error; return error; } + void clear_error() { m_error = 0; } + + int m_error = 0; + const Domain m_domain; + const Type m_type; + const int m_protocol; +}; diff --git a/kernel/net/UDPSocket.cpp b/kernel/net/UDPSocket.cpp new file mode 100644 index 00000000..42fb8d40 --- /dev/null +++ b/kernel/net/UDPSocket.cpp @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "UDPSocket.h" +#include "../kstd/KLog.h" +#include "../api/udp.h" + +#define UDP_DBG 1 + +kstd::map> UDPSocket::s_sockets; +Mutex UDPSocket::s_sockets_lock { "UDPSocket::sockets" }; + +UDPSocket::UDPSocket(): IPSocket(Type::Dgram, 0) { + +} + +UDPSocket::~UDPSocket() { + LOCK(s_sockets_lock); + if (m_bound && s_sockets.contains(m_port)) { + s_sockets.erase(m_port); + KLog::dbg_if("UDPSocket", "Unbinding from port %d", m_port); + } +} + +kstd::Arc UDPSocket::get_socket(uint16_t port) { + LOCK(s_sockets_lock); + auto sock = s_sockets.get(port); + if (!sock) + return kstd::Arc(nullptr); + auto locked = (*sock).lock(); + ASSERT(locked); + return locked; +} + +ResultRet> UDPSocket::make() { + return kstd::Arc(new UDPSocket()); +} + +Result UDPSocket::do_bind() { + LOCK(s_sockets_lock); + if (m_bound) + return Result(EINVAL); + if (s_sockets.contains(m_port)) + return Result(EADDRINUSE); + + KLog::dbg_if("UDPSocket", "Binding to port %d", m_port); + + // TODO: Device? IP? + + s_sockets[m_port] = self(); + m_bound = true; + + return Result(SUCCESS); +} + +ssize_t UDPSocket::do_recv(const IPv4Packet* pkt, SafePointer buf, size_t len) { + auto* udp_pkt = (const UDPPacket*) pkt->payload; + ASSERT(pkt->length >= sizeof(IPv4Packet) + sizeof(UDPPacket)); // Should've been rejected at IP layer + ASSERT(udp_pkt->len >= sizeof(UDPPacket)); // Should've been rejected in NetworkManager + + const size_t nread = min(len, udp_pkt->len.val() - sizeof(UDPPacket)); + buf.write(udp_pkt->payload, nread); + + return (ssize_t) nread; +} diff --git a/kernel/net/UDPSocket.h b/kernel/net/UDPSocket.h new file mode 100644 index 00000000..7e602967 --- /dev/null +++ b/kernel/net/UDPSocket.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include "IPSocket.h" + +class UDPSocket: public IPSocket, public kstd::ArcSelf { +public: + static ResultRet> make(); + + ~UDPSocket() override; + + static kstd::Arc get_socket(uint16_t port); + +protected: + UDPSocket(); + + Result do_bind() override; + ssize_t do_recv(const IPv4Packet *pkt, SafePointer buf, size_t len) override; + + static kstd::map> s_sockets; + static Mutex s_sockets_lock; +}; diff --git a/kernel/syscall/read_write.cpp b/kernel/syscall/read_write.cpp index 62f2f0ef..1df203d6 100644 --- a/kernel/syscall/read_write.cpp +++ b/kernel/syscall/read_write.cpp @@ -6,28 +6,52 @@ #include ssize_t Process::sys_read(int fd, UserspacePointer buf, size_t count) { - if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) + m_fd_lock.acquire(); + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) { + m_fd_lock.release(); return -EBADF; - return _file_descriptors[fd]->read(buf, count); + } + auto desc = _file_descriptors[fd]; + m_fd_lock.release(); + + return desc->read(buf, count); } -int Process::sys_readdir(int file, UserspacePointer buf, size_t len) { - if(file < 0 || file >= (int) _file_descriptors.size() || !_file_descriptors[file]) +int Process::sys_readdir(int fd, UserspacePointer buf, size_t len) { + m_fd_lock.acquire(); + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) { + m_fd_lock.release(); return -EBADF; - return _file_descriptors[file]->read_dir_entries(buf, len); + } + auto desc = _file_descriptors[fd]; + m_fd_lock.release(); + + return desc->read_dir_entries(buf, len); } ssize_t Process::sys_write(int fd, UserspacePointer buffer, size_t count) { - if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) + m_fd_lock.acquire(); + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) { + m_fd_lock.release(); return -EBADF; - ssize_t ret = _file_descriptors[fd]->write(buffer, count); + } + auto desc = _file_descriptors[fd]; + m_fd_lock.release(); + + ssize_t ret = desc->write(buffer, count); return ret; } -int Process::sys_lseek(int file, off_t off, int whence) { - if(file < 0 || file >= (int) _file_descriptors.size() || !_file_descriptors[file]) +int Process::sys_lseek(int fd, off_t off, int whence) { + m_fd_lock.acquire(); + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) { + m_fd_lock.release(); return -EBADF; - return _file_descriptors[file]->seek(off, whence); + } + auto desc = _file_descriptors[fd]; + m_fd_lock.release(); + + return desc->seek(off, whence); } int Process::sys_open(UserspacePointer filename, int options, int mode) { @@ -36,6 +60,7 @@ int Process::sys_open(UserspacePointer filename, int options, int mode) { auto fd_or_err = VFS::inst().open(path, options, mode & (~_umask), _user, _cwd); if(fd_or_err.is_error()) return fd_or_err.code(); + LOCK(m_fd_lock); _file_descriptors.push_back(fd_or_err.value()); fd_or_err.value()->set_owner(_self_ptr); fd_or_err.value()->set_path(path); @@ -43,9 +68,10 @@ int Process::sys_open(UserspacePointer filename, int options, int mode) { return (int)_file_descriptors.size() - 1; } -int Process::sys_close(int file) { - if(file < 0 || file >= (int) _file_descriptors.size() || !_file_descriptors[file]) +int Process::sys_close(int fd) { + LOCK(m_fd_lock); + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) return -EBADF; - _file_descriptors[file] = kstd::Arc(nullptr); + _file_descriptors[fd] = kstd::Arc(nullptr); return 0; } \ No newline at end of file diff --git a/kernel/syscall/socket.cpp b/kernel/syscall/socket.cpp new file mode 100644 index 00000000..06568b7c --- /dev/null +++ b/kernel/syscall/socket.cpp @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "../tasking/Process.h" +#include "../memory/SafePointer.h" +#include "../net/Socket.h" +#include "../filesystem/FileDescriptor.h" + +int Process::sys_socket(int domain, int type, int protocol) { + auto socket_res = Socket::make((Socket::Domain) domain, (Socket::Type) type, protocol); + if (socket_res.is_error()) + return -socket_res.code(); + auto fd = kstd::make_shared(socket_res.value(), this); + + LOCK(m_fd_lock); + _file_descriptors.push_back(fd); + fd->set_owner(_self_ptr); + fd->set_id((int) _file_descriptors.size() - 1); + return (int)_file_descriptors.size() - 1; +} + +#define get_socket(fd) \ + m_fd_lock.acquire(); \ + if(fd < 0 || fd >= (int) _file_descriptors.size() || !_file_descriptors[fd]) { \ + m_fd_lock.release(); \ + return -EBADF; \ + } \ + auto desc = _file_descriptors[fd]; \ + if (!desc->file()->is_socket()) \ + return -ENOTSOCK; \ + auto socket = kstd::static_pointer_cast(desc->file()); + +int Process::sys_bind(int sockfd, UserspacePointer addr, uint32_t addrlen) { + get_socket(sockfd); + return -socket->bind(addr, addrlen).code(); +} + +int Process::sys_setsockopt(UserspacePointer ptr) { + return -1; +} + +int Process::sys_getsockopt(UserspacePointer ptr) { + return -1; +} + +int Process::sys_recvmsg(int sockfd, UserspacePointer msg_ptr, int flags) { + get_socket(sockfd); + auto msg = msg_ptr.get(); + + // TODO: More than one entry in iovec + if (msg.msg_iovlen != 1) + return -EINVAL; + + auto iov = UserspacePointer(msg.msg_iov).get(); + auto addr_ptr = UserspacePointer((sockaddr*) msg.msg_name); + auto addrlen_ptr = UserspacePointer(&msg_ptr.raw()->msg_namelen); + auto buf = UserspacePointer((uint8_t*) iov.iov_base); + + // TODO: Control messages + UserspacePointer(&msg_ptr.raw()->msg_controllen).set(0); + + return socket->recvfrom(*desc, buf, iov.iov_len, flags, addr_ptr, addrlen_ptr); +} + +int Process::sys_sendmsg(int sockfd, UserspacePointer msg, int flags) { + return -1; +} \ No newline at end of file diff --git a/kernel/syscall/syscall.cpp b/kernel/syscall/syscall.cpp index 9a3c0ca6..2a1ee7aa 100644 --- a/kernel/syscall/syscall.cpp +++ b/kernel/syscall/syscall.cpp @@ -182,6 +182,18 @@ int handle_syscall(ThreadRegisters& regs, uint32_t call, uint32_t arg1, uint32_t return cur_proc->sys_mprotect((void*) arg1, (size_t) arg2, arg3); case SYS_UNAME: return cur_proc->sys_uname((struct utsname*) arg1); + case SYS_SOCKET: + return cur_proc->sys_socket(arg1, arg2, arg3); + case SYS_BIND: + return cur_proc->sys_bind(arg1, (struct sockaddr*) arg2, arg3); + case SYS_SETSOCKOPT: + return cur_proc->sys_setsockopt((struct setsockopt_args*) arg1); + case SYS_GETSOCKOPT: + return cur_proc->sys_getsockopt((struct getsockopt_args*) arg1); + case SYS_SENDMSG: + return cur_proc->sys_sendmsg(arg1, (struct msghdr*) arg2, arg3); + case SYS_RECVMSG: + return cur_proc->sys_recvmsg(arg1, (struct msghdr*) arg2, arg3); //TODO: Implement these syscalls case SYS_TIMES: diff --git a/kernel/syscall/syscall_numbers.h b/kernel/syscall/syscall_numbers.h index 29754490..06f7bc16 100644 --- a/kernel/syscall/syscall_numbers.h +++ b/kernel/syscall/syscall_numbers.h @@ -79,6 +79,12 @@ #define SYS_MPROTECT 76 #define SYS_UNAME 77 #define SYS_PTRACE 78 +#define SYS_SOCKET 79 +#define SYS_BIND 80 +#define SYS_SETSOCKOPT 81 +#define SYS_GETSOCKOPT 82 +#define SYS_RECVMSG 83 +#define SYS_SENDMSG 84 #ifndef DUCKOS_KERNEL #include @@ -89,4 +95,20 @@ struct readlinkat_args { const char* path; char* buf; size_t bufsize; +}; + +struct getsockopt_args { + int sockfd; + int level; + int option_name; + void* option_value; + uint32_t* option_len; +}; + +struct setsockopt_args { + int sockfd; + int level; + int option_name; + const void* option_value; + uint32_t option_len; }; \ No newline at end of file diff --git a/kernel/tasking/Process.h b/kernel/tasking/Process.h index f8fda20e..e9f908da 100644 --- a/kernel/tasking/Process.h +++ b/kernel/tasking/Process.h @@ -167,6 +167,12 @@ class Process { int sys_munmap(void* addr, size_t length); int sys_mprotect(void* addr, size_t length, int prot); int sys_uname(UserspacePointer buf); + int sys_socket(int domain, int type, int protocol); + int sys_bind(int sockfd, UserspacePointer addr, uint32_t addrlen); + int sys_setsockopt(UserspacePointer ptr); + int sys_getsockopt(UserspacePointer ptr); + int sys_recvmsg(int sockfd, UserspacePointer msg, int flags); + int sys_sendmsg(int sockfd, UserspacePointer msg, int flags); private: friend class Thread; @@ -219,6 +225,7 @@ class Process { size_t m_used_shmem = 0; //Files & Pipes + Mutex m_fd_lock { "Process::FileDescriptor" }; kstd::vector> _file_descriptors; kstd::Arc _cwd; diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 85d2d4fd..00db9eb6 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -1,4 +1,4 @@ -ADD_COMPILE_OPTIONS(-O2) +ADD_COMPILE_OPTIONS(-O3 -msse2) ADD_SUBDIRECTORY(libc/) ADD_SUBDIRECTORY(libm/) ADD_SUBDIRECTORY(libpond/) diff --git a/libraries/libc/CMakeLists.txt b/libraries/libc/CMakeLists.txt index feb77adf..3c973e99 100644 --- a/libraries/libc/CMakeLists.txt +++ b/libraries/libc/CMakeLists.txt @@ -1,4 +1,5 @@ SET(SOURCES + arpa/inet.cpp assert.c crt0.c ctype.c @@ -20,6 +21,7 @@ SET(SOURCES sys/ptrace.c sys/liballoc.cpp sys/scanf.c + sys/socket.c sys/socketfs.c sys/stat.c sys/status.c diff --git a/libraries/libc/arpa/inet.cpp b/libraries/libc/arpa/inet.cpp new file mode 100644 index 00000000..ede6c4f9 --- /dev/null +++ b/libraries/libc/arpa/inet.cpp @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "inet.h" +#include + +uint32_t htonl(uint32_t hostlong) { + return as_big_endian(hostlong); +} + +uint16_t htons(uint16_t hostshort) { + return as_big_endian(hostshort); +} + +uint32_t ntohl(uint32_t netlong) { + return from_big_endian(netlong); +} + +uint16_t ntohs(uint16_t netshort) { + return from_big_endian(netshort); +} \ No newline at end of file diff --git a/libraries/libc/arpa/inet.h b/libraries/libc/arpa/inet.h new file mode 100644 index 00000000..6733eaa0 --- /dev/null +++ b/libraries/libc/arpa/inet.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include +#include "../stdint.h" + +__DECL_BEGIN + +uint32_t htonl(uint32_t hostlong); +uint16_t htons(uint16_t hostshort); +uint32_t ntohl(uint32_t netlong); +uint16_t ntohs(uint16_t netshort); + +__DECL_END \ No newline at end of file diff --git a/libraries/libc/netinet/in.h b/libraries/libc/netinet/in.h new file mode 100644 index 00000000..7bed5a89 --- /dev/null +++ b/libraries/libc/netinet/in.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once +#include \ No newline at end of file diff --git a/libraries/libc/string.c b/libraries/libc/string.c index cfde29fa..8538e28d 100644 --- a/libraries/libc/string.c +++ b/libraries/libc/string.c @@ -382,6 +382,10 @@ char* strerror(int errnum) { return "No message of desired type"; case EIDRM: return "Identifier removed"; + case EADDRINUSE: + return "Address in use"; + case ENOTSOCK: + return "File descriptor is not a socket"; default: return "Unknown error"; } diff --git a/libraries/libc/sys/socket.c b/libraries/libc/sys/socket.c new file mode 100644 index 00000000..be450f85 --- /dev/null +++ b/libraries/libc/sys/socket.c @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#include "socket.h" +#include "syscall.h" +#include "../stdlib.h" +#include "errno.h" +#include "../string.h" + +int socket(int domain, int type, int protocol) { + return syscall4(SYS_SOCKET, domain, type, protocol); +} + +int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { + return syscall4(SYS_BIND, sockfd, (int) addr, addrlen); +} + +int setsockopt(int sockfd, int level, int option, const void* option_value, socklen_t option_len) { + struct setsockopt_args args = { sockfd, level, option, option_value, option_len }; + return syscall2(SYS_SETSOCKOPT, (int) &args); +} + +int getsockopt(int sockfd, int level, int option, void* option_value, socklen_t* option_len) { + struct getsockopt_args args = { sockfd, level, option, option_value, option_len }; + return syscall2(SYS_GETSOCKOPT, (int) &args); +} + +ssize_t send(int sockfd, const void *buf, size_t len, int flags) { + return sendto(sockfd, buf, len, flags, NULL, NULL); +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { + struct iovec iov = {(void*) buf, len}; + struct msghdr msg = {(struct sockaddr*) dest_addr, addrlen, &iov, 1, NULL, 0, 0}; + return sendmsg(sockfd, &msg, flags); +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + return syscall4(SYS_SENDMSG, sockfd, (int) msg, flags); +} + +ssize_t recv(int sockfd, void *buf, size_t len, int flags) { + return recvfrom(sockfd, buf, len, flags, NULL, NULL); +} + +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { + if (!addrlen && src_addr) { + errno = EINVAL; + return -1; + } + + struct sockaddr_storage addr; + struct iovec iov = {buf, len}; + struct msghdr msg = {src_addr ? &addr : NULL, src_addr ? sizeof(addr) : 0, &iov, 1, NULL, 0, 0}; + ssize_t nread = recvmsg(sockfd, &msg, flags); + if (src_addr && nread >= 0) { + memcpy(src_addr, &addr, *addrlen < msg.msg_namelen ? *addrlen : msg.msg_namelen); + *addrlen = msg.msg_namelen; + } + return nread; +} + +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { + return syscall4(SYS_RECVMSG, sockfd, (int) msg, flags); +} \ No newline at end of file diff --git a/libraries/libc/sys/socket.h b/libraries/libc/sys/socket.h index 03a1b15e..2a86b841 100644 --- a/libraries/libc/sys/socket.h +++ b/libraries/libc/sys/socket.h @@ -1,4 +1,22 @@ /* Workaround for GCC compilation */ #ifdef CODY_NETWORKING #define CODY_NETWORKING 0 -#endif \ No newline at end of file +#endif + +#include +#include "../netinet/in.h" +#include "types.h" +#include "un.h" + +__DECL_BEGIN +int socket(int domain, int type, int protocol); +int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen); +int setsockopt(int sockfd, int level, int option, const void* option_value, socklen_t option_len); +int getsockopt(int sockfd, int level, int option, void* option_value, socklen_t* option_len); +ssize_t send(int sockfd, const void *buf, size_t len, int flags); +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); +ssize_t recv(int sockfd, void *buf, size_t len, int flags); +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); +__DECL_END \ No newline at end of file diff --git a/libraries/libc/sys/un.h b/libraries/libc/sys/un.h new file mode 100644 index 00000000..ea647e32 --- /dev/null +++ b/libraries/libc/sys/un.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2024 Byteduck */ + +#pragma once + +#include \ No newline at end of file