Skip to content

Commit

Permalink
Kernel: Basic userspace socket functionality
Browse files Browse the repository at this point in the history
Can only receive UDP packets so far. Much work remains to be done
  • Loading branch information
byteduck committed Mar 13, 2024
1 parent 6537543 commit 88b91a1
Show file tree
Hide file tree
Showing 32 changed files with 807 additions and 18 deletions.
6 changes: 5 additions & 1 deletion kernel/CMakeLists.txt
Expand Up @@ -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
Expand All @@ -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"
Expand Down
29 changes: 29 additions & 0 deletions 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
7 changes: 7 additions & 0 deletions kernel/api/ipv4.h
Expand Up @@ -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;
Expand Down
52 changes: 52 additions & 0 deletions 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
16 changes: 16 additions & 0 deletions 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<uint16_t> source_port;
BigEndian<uint16_t> dest_port;
BigEndian<uint16_t> len;
BigEndian<uint16_t> checksum = 0;
uint8_t payload[];
} __attribute__((packed));

static_assert(sizeof(UDPPacket) == 8);
17 changes: 17 additions & 0 deletions 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
4 changes: 4 additions & 0 deletions kernel/filesystem/File.cpp
Expand Up @@ -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<uint8_t> buffer, size_t count) {
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions kernel/filesystem/File.h
Expand Up @@ -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<void*> argp);
virtual void open(FileDescriptor& fd, int options);
virtual void close(FileDescriptor& fd);
Expand Down
70 changes: 70 additions & 0 deletions 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<typename T, size_t max_count>
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<T> 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<T> 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;
};

}
10 changes: 8 additions & 2 deletions kernel/memory/SafePointer.h
Expand Up @@ -9,6 +9,7 @@
template<typename T>
class SafePointer {
public:
SafePointer() = default;
explicit SafePointer(T* raw_ptr, bool is_user):
m_ptr(raw_ptr), m_is_user(is_user) {}
template<typename C> SafePointer(const SafePointer<C>& safe_ptr):
Expand Down Expand Up @@ -153,9 +154,14 @@ class SafePointer {
});
}

template<typename R>
SafePointer<R> as() {
return SafePointer<R>((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<typename T>
Expand Down
87 changes: 87 additions & 0 deletions 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<kstd::Arc<IPSocket>> IPSocket::make(Socket::Type type, int protocol) {
switch (type) {
case Type::Dgram:
return kstd::static_pointer_cast<IPSocket>(TRY(UDPSocket::make()));
default:
return Result(EINVAL);
}
}

Result IPSocket::bind(SafePointer<sockaddr> addr_ptr, socklen_t addrlen) {
if (m_bound || addrlen != sizeof(sockaddr_in))
return Result(set_error(EINVAL));

auto addr = addr_ptr.as<sockaddr_in>().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<uint8_t> buf, size_t len, int flags, SafePointer<sockaddr> src_addr, SafePointer<socklen_t> 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());
}
36 changes: 36 additions & 0 deletions 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<kstd::Arc<IPSocket>> make(Socket::Type type, int protocol);

// Socket
Result bind(SafePointer<sockaddr> addr, socklen_t addrlen) override;
ssize_t recvfrom(FileDescriptor &fd, SafePointer<uint8_t> buf, size_t len, int flags, SafePointer<sockaddr> src_addr, SafePointer<socklen_t> 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<uint8_t> 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<IPv4Packet*> m_receive_queue { 16 };
Mutex m_receive_queue_lock { "IPSocket::receive_queue" };
BooleanBlocker m_receive_blocker;
};

0 comments on commit 88b91a1

Please sign in to comment.