Skip to content

Commit

Permalink
Kernel: TCP functionality (WIP)
Browse files Browse the repository at this point in the history
Still need to retransmit unacked packets, and we probably need tests for this stuff. But, it works for both serving and receiving TCP packets, hooray!
  • Loading branch information
byteduck committed Mar 27, 2024
1 parent abb0702 commit 6b82ea5
Show file tree
Hide file tree
Showing 33 changed files with 1,214 additions and 103 deletions.
2 changes: 2 additions & 0 deletions kernel/CMakeLists.txt
Expand Up @@ -91,6 +91,7 @@ SET(KERNEL_SRCS
tasking/BooleanBlocker.cpp
tasking/PollBlocker.cpp
tasking/SleepBlocker.cpp
tasking/FileBlockers.cpp
device/VGADevice.cpp
device/BochsVGADevice.cpp
device/MultibootVGADevice.cpp
Expand Down Expand Up @@ -181,6 +182,7 @@ SET(KERNEL_SRCS
net/Socket.cpp
net/IPSocket.cpp
net/UDPSocket.cpp
net/TCPSocket.cpp
net/Router.cpp)

add_custom_command(
Expand Down
2 changes: 2 additions & 0 deletions kernel/Result.cpp
Expand Up @@ -20,6 +20,8 @@
#include "Result.hpp"
#include "kernel/kstd/kstdio.h"

const Result Result::Success {0};

Result::Result(int code): _code(code) {
}

Expand Down
10 changes: 9 additions & 1 deletion kernel/Result.hpp
Expand Up @@ -21,6 +21,7 @@

#include <kernel/kstd/Arc.h>
#include <kernel/kstd/kstdio.h>
#include <kernel/kstd/type_traits.h>

#define TRY(expr) \
({ \
Expand All @@ -30,9 +31,16 @@
res.value(); \
})

#define TRYRES(expr) \
({ \
auto res = (expr); \
if (res.is_error()) \
return res; \
})

class Result {
public:
static const int Success = 0;
static const Result Success;

explicit Result(int code);

Expand Down
12 changes: 12 additions & 0 deletions kernel/api/ipv4.h
Expand Up @@ -44,6 +44,18 @@ class __attribute__((packed)) IPv4Address {
return m_data < other.m_data;
}

inline constexpr bool operator<=(const IPv4Address& other) const {
return m_data <= other.m_data;
}

inline constexpr bool operator>(const IPv4Address& other) const {
return m_data > other.m_data;
}

inline constexpr bool operator>=(const IPv4Address& other) const {
return m_data >= other.m_data;
}

inline sockaddr_in as_sockaddr(in_port_t port) const {
sockaddr_in ret;
ret.sin_family = AF_INET;
Expand Down
9 changes: 9 additions & 0 deletions kernel/api/socket.h
Expand Up @@ -28,6 +28,15 @@ __DECL_BEGIN
#define SO_BROADCAST 2
#define SO_ERROR 3

// shutdown
#define SHUT_RD 0x1
#define SHUT_WR 0x2
#define SHUT_RDWR (SHUT_RD | SHUT_WR)

// flags
#define SOCK_NONBLOCK 0x1
#define SOCK_CLOEXEC 0x2

typedef uint16_t sa_family_t;
typedef uint32_t socklen_t;

Expand Down
106 changes: 106 additions & 0 deletions kernel/api/tcp.h
@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright © 2016-2024 Byteduck */

#pragma once

#include "endian.h"

#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20

enum TCPOption {
End = 0,
Nop = 1,
MSS = 2,
WindowScale = 3,
SACKPermit = 4,
SACK = 5,
Timestamp = 6
};

struct TCPSegment {
BigEndian<uint16_t> source_port;
BigEndian<uint16_t> dest_port;
BigEndian<uint32_t> sequence;
BigEndian<uint32_t> ack;
BigEndian<uint16_t> flags_and_offset;
BigEndian<uint16_t> window_size;
BigEndian<uint16_t> checksum;
BigEndian<uint16_t> urgent_pointer;
uint8_t data[];

[[nodiscard]] uint8_t data_offset() const { return (flags_and_offset.val() & 0xf000) >> 12; }
void set_data_offset(uint8_t offset) { flags_and_offset = (flags_and_offset & 0xfff) | ((uint16_t) offset) << 12; }

[[nodiscard]] uint8_t flags() const { return flags_and_offset.val() & 0x01ff; }
void set_flags(uint16_t flags) { flags_and_offset = (flags_and_offset & ~0x01ff) | (flags & 0x1ff); }

[[nodiscard]] const uint8_t* payload() const { return ((const uint8_t*) this) + (data_offset() * sizeof(uint32_t)); }
[[nodiscard]] uint8_t* payload() { return ((uint8_t*) this) + (data_offset() * sizeof(uint32_t)); }

inline BigEndian<uint16_t> calculate_checksum(const IPv4Address& src, const IPv4Address& dest, size_t payload_size) {
union PseudoHeader {
struct __attribute__((packed)) {
IPv4Address src;
IPv4Address dest;
uint8_t zero;
uint8_t proto;
BigEndian<uint16_t> payload_size;
} data;
uint16_t raw[6] = {0};
};
static_assert(sizeof(PseudoHeader::data) == sizeof(PseudoHeader::raw));

PseudoHeader pheader = {
.data = {
.src = src,
.dest = dest,
.zero = 0,
.proto = IPv4Proto::TCP,
.payload_size = data_offset() * sizeof(uint32_t) + payload_size
}
};

uint32_t sum = 0;

// Checksum of pseudo header
auto* ptr = (uint16_t*) pheader.raw;
for (size_t i = 0; i < sizeof(pheader) / sizeof(uint16_t); i++) {
sum += as_big_endian(ptr[i]);
if (sum > 0xffff)
sum = (sum >> 16) + (sum & 0xffff);
}

// Checksum of segment header
const void* selfptr = this; // Necessary to suppress alignment errors
ptr = (uint16_t*) selfptr;
for (size_t i = 0; i < (data_offset() * sizeof(uint32_t)) / sizeof(uint16_t); i++) {
sum += as_big_endian(ptr[i]);
if (sum > 0xffff)
sum = (sum >> 16) + (sum & 0xffff);
}

// Checksum of payload
ptr = (uint16_t*) payload();
for (size_t i = 0; i < payload_size / sizeof(uint16_t); i++) {
sum += as_big_endian(ptr[i]);
if (sum > 0xffff)
sum = (sum >> 16) + (sum & 0xffff);
}

// Pad
if (payload_size % 2 != 0) {
sum += ((uint32_t) payload()[payload_size - 1]) << 8;
if (sum > 0xffff)
sum = (sum >> 16) + (sum & 0xffff);
}

return ~(sum & 0xffff);
}
} __attribute__((packed));

static_assert(sizeof(TCPSegment) == 20);
9 changes: 3 additions & 6 deletions kernel/net/E1000Adapter.cpp
Expand Up @@ -112,7 +112,7 @@
#define INT_RXO 0x40 // Receiver Overrun
#define INT_RXT0 0x80 // Receiver Timer Interrupt

#define E1000_DBG true
#define E1000_DBG false

void E1000Adapter::probe() {
PCI::enumerate_devices([](PCI::Address address, PCI::ID id, uint16_t type, void* dataPtr) {
Expand Down Expand Up @@ -266,15 +266,12 @@ void E1000Adapter::receive() {
ASSERT(desc.length <= rx_buffer_size);
KLog::dbg_if<E1000_DBG>("E1000", "Received packet ({} bytes)", desc.length);
desc.status = 0;
{
TaskManager::ScopedCritical crit;
receive_bytes(KernelPointer((uint8_t*) (m_rx_buffer_region->start() + (rx_buffer_size * cur_desc))), desc.length);
}
receive_bytes({(uint8_t*) (m_rx_buffer_region->start() + (rx_buffer_size * cur_desc)), desc.length}, desc.length);
m_window.out32(REG_RXDESCTAIL, cur_desc);
}
}

void E1000Adapter::send_bytes(SafePointer<uint8_t> bytes, size_t count) {
void E1000Adapter::send_bytes(const ReadableBytes& bytes, size_t count) {
ASSERT(count <= tx_buffer_size);
PCI::disable_interrupt(m_pci_address);
auto cur_tx_desc = m_window.in32(REG_TXDESCTAIL) % num_tx_descriptors;
Expand Down
2 changes: 1 addition & 1 deletion kernel/net/E1000Adapter.h
Expand Up @@ -15,7 +15,7 @@ class E1000Adapter: public NetworkAdapter, IRQHandler {

protected:
void handle_irq(IRQRegisters *regs) override;
void send_bytes(SafePointer<uint8_t> bytes, size_t count) override;
void send_bytes(const ReadableBytes &bytes, size_t count) override;

private:
explicit E1000Adapter(PCI::Address addr);
Expand Down
2 changes: 1 addition & 1 deletion kernel/net/ICMP.h
Expand Up @@ -3,7 +3,7 @@

#pragma once

enum ICMPType {
enum class ICMPType {
EchoReply = 0,
DestinationUnreachable = 3,
SourceQuench = 4,
Expand Down

0 comments on commit 6b82ea5

Please sign in to comment.