1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 06:07:44 +00:00

Kernel: Move networking related files into Kernel/Net/.

This commit is contained in:
Andreas Kling 2019-04-02 19:54:38 +02:00
parent 718bea73b3
commit 649c81a714
26 changed files with 61 additions and 63 deletions

61
Kernel/Net/ARP.h Normal file
View file

@ -0,0 +1,61 @@
#pragma once
#include <Kernel/Net/MACAddress.h>
#include <Kernel/Net/IPv4.h>
#include <Kernel/Net/EtherType.h>
struct ARPOperation {
enum : word {
Request = 1,
Response = 2,
};
};
struct ARPHardwareType {
enum : word {
Ethernet = 1,
};
};
class [[gnu::packed]] ARPPacket {
public:
word hardware_type() const { return ntohs(m_hardware_type); }
void set_hardware_type(word w) { m_hardware_type = htons(w); }
word protocol_type() const { return ntohs(m_protocol_type); }
void set_protocol_type(word w) { m_protocol_type = htons(w); }
byte hardware_address_length() const { return m_hardware_address_length; }
void set_hardware_address_length(byte b) { m_hardware_address_length = b; }
byte protocol_address_length() const { return m_protocol_address_length; }
void set_protocol_address_length(byte b) { m_protocol_address_length = b; }
word operation() const { return ntohs(m_operation); }
void set_operation(word w) { m_operation = htons(w); }
const MACAddress& sender_hardware_address() const { return m_sender_hardware_address; }
void set_sender_hardware_address(const MACAddress& address) { m_sender_hardware_address = address; }
const IPv4Address& sender_protocol_address() const { return m_sender_protocol_address; }
void set_sender_protocol_address(const IPv4Address& address) { m_sender_protocol_address = address; }
const MACAddress& target_hardware_address() const { return m_target_hardware_address; }
void set_target_hardware_address(const MACAddress& address) { m_target_hardware_address = address; }
const IPv4Address& target_protocol_address() const { return m_target_protocol_address; }
void set_target_protocol_address(const IPv4Address& address) { m_target_protocol_address = address; }
private:
word m_hardware_type { 0x0100 };
word m_protocol_type { 0x0008 };
byte m_hardware_address_length { sizeof(MACAddress) };
byte m_protocol_address_length { sizeof(IPv4Address) };
word m_operation { 0 };
MACAddress m_sender_hardware_address;
IPv4Address m_sender_protocol_address;
MACAddress m_target_hardware_address;
IPv4Address m_target_protocol_address;
};
static_assert(sizeof(ARPPacket) == 28);

View file

@ -0,0 +1,354 @@
#include <Kernel/Net/E1000NetworkAdapter.h>
#include <Kernel/PCI.h>
#include <Kernel/IO.h>
#define REG_CTRL 0x0000
#define REG_STATUS 0x0008
#define REG_EEPROM 0x0014
#define REG_CTRL_EXT 0x0018
#define REG_IMASK 0x00D0
#define REG_RCTRL 0x0100
#define REG_RXDESCLO 0x2800
#define REG_RXDESCHI 0x2804
#define REG_RXDESCLEN 0x2808
#define REG_RXDESCHEAD 0x2810
#define REG_RXDESCTAIL 0x2818
#define REG_TCTRL 0x0400
#define REG_TXDESCLO 0x3800
#define REG_TXDESCHI 0x3804
#define REG_TXDESCLEN 0x3808
#define REG_TXDESCHEAD 0x3810
#define REG_TXDESCTAIL 0x3818
#define REG_RDTR 0x2820 // RX Delay Timer Register
#define REG_RXDCTL 0x3828 // RX Descriptor Control
#define REG_RADV 0x282C // RX Int. Absolute Delay Timer
#define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt
#define REG_TIPG 0x0410 // Transmit Inter Packet Gap
#define ECTRL_SLU 0x40 //set link up
#define RCTL_EN (1 << 1) // Receiver Enable
#define RCTL_SBP (1 << 2) // Store Bad Packets
#define RCTL_UPE (1 << 3) // Unicast Promiscuous Enabled
#define RCTL_MPE (1 << 4) // Multicast Promiscuous Enabled
#define RCTL_LPE (1 << 5) // Long Packet Reception Enable
#define RCTL_LBM_NONE (0 << 6) // No Loopback
#define RCTL_LBM_PHY (3 << 6) // PHY or external SerDesc loopback
#define RTCL_RDMTS_HALF (0 << 8) // Free Buffer Threshold is 1/2 of RDLEN
#define RTCL_RDMTS_QUARTER (1 << 8) // Free Buffer Threshold is 1/4 of RDLEN
#define RTCL_RDMTS_EIGHTH (2 << 8) // Free Buffer Threshold is 1/8 of RDLEN
#define RCTL_MO_36 (0 << 12) // Multicast Offset - bits 47:36
#define RCTL_MO_35 (1 << 12) // Multicast Offset - bits 46:35
#define RCTL_MO_34 (2 << 12) // Multicast Offset - bits 45:34
#define RCTL_MO_32 (3 << 12) // Multicast Offset - bits 43:32
#define RCTL_BAM (1 << 15) // Broadcast Accept Mode
#define RCTL_VFE (1 << 18) // VLAN Filter Enable
#define RCTL_CFIEN (1 << 19) // Canonical Form Indicator Enable
#define RCTL_CFI (1 << 20) // Canonical Form Indicator Bit Value
#define RCTL_DPF (1 << 22) // Discard Pause Frames
#define RCTL_PMCF (1 << 23) // Pass MAC Control Frames
#define RCTL_SECRC (1 << 26) // Strip Ethernet CRC
// Buffer Sizes
#define RCTL_BSIZE_256 (3 << 16)
#define RCTL_BSIZE_512 (2 << 16)
#define RCTL_BSIZE_1024 (1 << 16)
#define RCTL_BSIZE_2048 (0 << 16)
#define RCTL_BSIZE_4096 ((3 << 16) | (1 << 25))
#define RCTL_BSIZE_8192 ((2 << 16) | (1 << 25))
#define RCTL_BSIZE_16384 ((1 << 16) | (1 << 25))
// Transmit Command
#define CMD_EOP (1 << 0) // End of Packet
#define CMD_IFCS (1 << 1) // Insert FCS
#define CMD_IC (1 << 2) // Insert Checksum
#define CMD_RS (1 << 3) // Report Status
#define CMD_RPS (1 << 4) // Report Packet Sent
#define CMD_VLE (1 << 6) // VLAN Packet Enable
#define CMD_IDE (1 << 7) // Interrupt Delay Enable
// TCTL Register
#define TCTL_EN (1 << 1) // Transmit Enable
#define TCTL_PSP (1 << 3) // Pad Short Packets
#define TCTL_CT_SHIFT 4 // Collision Threshold
#define TCTL_COLD_SHIFT 12 // Collision Distance
#define TCTL_SWXOFF (1 << 22) // Software XOFF Transmission
#define TCTL_RTLC (1 << 24) // Re-transmit on Late Collision
#define TSTA_DD (1 << 0) // Descriptor Done
#define TSTA_EC (1 << 1) // Excess Collisions
#define TSTA_LC (1 << 2) // Late Collision
#define LSTA_TU (1 << 3) // Transmit Underrun
OwnPtr<E1000NetworkAdapter> E1000NetworkAdapter::autodetect()
{
static const PCI::ID qemu_bochs_vbox_id = { 0x8086, 0x100e };
PCI::Address found_address;
PCI::enumerate_all([&] (const PCI::Address& address, PCI::ID id) {
if (id == qemu_bochs_vbox_id) {
found_address = address;
return;
}
});
if (found_address.is_null())
return nullptr;
byte irq = PCI::get_interrupt_line(found_address);
return make<E1000NetworkAdapter>(found_address, irq);
}
static E1000NetworkAdapter* s_the;
E1000NetworkAdapter* E1000NetworkAdapter::the()
{
return s_the;
}
E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address pci_address, byte irq)
: IRQHandler(irq)
, m_pci_address(pci_address)
{
s_the = this;
kprintf("E1000: Found at PCI address %b:%b:%b\n", pci_address.bus(), pci_address.slot(), pci_address.function());
enable_bus_mastering(m_pci_address);
m_mmio_base = PhysicalAddress(PCI::get_BAR0(m_pci_address));
MM.map_for_kernel(LinearAddress(m_mmio_base.get()), m_mmio_base);
MM.map_for_kernel(LinearAddress(m_mmio_base.offset(4096).get()), m_mmio_base.offset(4096));
MM.map_for_kernel(LinearAddress(m_mmio_base.offset(8192).get()), m_mmio_base.offset(8192));
MM.map_for_kernel(LinearAddress(m_mmio_base.offset(12288).get()), m_mmio_base.offset(12288));
MM.map_for_kernel(LinearAddress(m_mmio_base.offset(16384).get()), m_mmio_base.offset(16384));
m_use_mmio = true;
m_io_base = PCI::get_BAR1(m_pci_address) & ~1;
m_interrupt_line = PCI::get_interrupt_line(m_pci_address);
kprintf("E1000: IO port base: %w\n", m_io_base);
kprintf("E1000: MMIO base: P%x\n", m_mmio_base);
kprintf("E1000: Interrupt line: %u\n", m_interrupt_line);
detect_eeprom();
kprintf("E1000: Has EEPROM? %u\n", m_has_eeprom);
read_mac_address();
const auto& mac = mac_address();
kprintf("E1000: MAC address: %b:%b:%b:%b:%b:%b\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
dword flags = in32(REG_CTRL);
out32(REG_CTRL, flags | ECTRL_SLU);
initialize_rx_descriptors();
initialize_tx_descriptors();
out32(REG_IMASK, 0x1f6dc);
out32(REG_IMASK, 0xff & ~4);
in32(0xc0);
enable_irq();
}
E1000NetworkAdapter::~E1000NetworkAdapter()
{
}
void E1000NetworkAdapter::handle_irq()
{
out32(REG_IMASK, 0x1);
dword status = in32(0xc0);
if (status & 4) {
dword flags = in32(REG_CTRL);
out32(REG_CTRL, flags | ECTRL_SLU);
}
if (status & 0x10) {
// Threshold OK?
}
if (status & 0x80) {
receive();
}
}
void E1000NetworkAdapter::detect_eeprom()
{
out32(REG_EEPROM, 0x1);
for (volatile int i = 0; i < 999; ++i) {
dword data = in32(REG_EEPROM);
if (data & 0x10) {
m_has_eeprom = true;
return;
}
}
m_has_eeprom = false;
}
dword E1000NetworkAdapter::read_eeprom(byte address)
{
word data = 0;
dword tmp = 0;
if (m_has_eeprom) {
out32(REG_EEPROM, ((dword)address << 8) | 1);
while (!((tmp = in32(REG_EEPROM)) & (1 << 4)))
;
} else {
out32(REG_EEPROM, ((dword)address << 2) | 1);
while (!((tmp = in32(REG_EEPROM)) & (1 << 1)))
;
}
data = (tmp >> 16) & 0xffff;
return data;
}
void E1000NetworkAdapter::read_mac_address()
{
if (m_has_eeprom) {
byte mac[6];
dword tmp = read_eeprom(0);
mac[0] = tmp & 0xff;
mac[1] = tmp >> 8;
tmp = read_eeprom(1);
mac[2] = tmp & 0xff;
mac[3] = tmp >> 8;
tmp = read_eeprom(2);
mac[4] = tmp & 0xff;
mac[5] = tmp >> 8;
set_mac_address(mac);
} else {
ASSERT_NOT_REACHED();
}
}
void E1000NetworkAdapter::initialize_rx_descriptors()
{
auto ptr = (dword)kmalloc_eternal(sizeof(e1000_rx_desc) * number_of_rx_descriptors + 16);
// Make sure it's 16-byte aligned.
if (ptr % 16)
ptr = (ptr + 16) - (ptr % 16);
m_rx_descriptors = (e1000_rx_desc*)ptr;
for (int i = 0; i < number_of_rx_descriptors; ++i) {
auto& descriptor = m_rx_descriptors[i];
descriptor.addr = (qword)kmalloc_eternal(8192 + 16);
descriptor.status = 0;
}
out32(REG_RXDESCLO, ptr);
out32(REG_RXDESCHI, 0);
out32(REG_RXDESCLEN, number_of_rx_descriptors * sizeof(e1000_rx_desc));
out32(REG_RXDESCHEAD, 0);
out32(REG_RXDESCTAIL, number_of_rx_descriptors - 1);
out32(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192);
}
void E1000NetworkAdapter::initialize_tx_descriptors()
{
auto ptr = (dword)kmalloc_eternal(sizeof(e1000_tx_desc) * number_of_tx_descriptors + 16);
// Make sure it's 16-byte aligned.
if (ptr % 16)
ptr = (ptr + 16) - (ptr % 16);
m_tx_descriptors = (e1000_tx_desc*)ptr;
for (int i = 0; i < number_of_tx_descriptors; ++i) {
auto& descriptor = m_tx_descriptors[i];
descriptor.addr = (qword)kmalloc_eternal(8192 + 16);
descriptor.cmd = 0;
}
out32(REG_TXDESCLO, ptr);
out32(REG_TXDESCHI, 0);
out32(REG_TXDESCLEN, number_of_tx_descriptors * sizeof(e1000_tx_desc));
out32(REG_TXDESCHEAD, 0);
out32(REG_TXDESCTAIL, 0);
out32(REG_TCTRL, in32(REG_TCTRL) | TCTL_EN | TCTL_PSP);
out32(REG_TIPG, 0x0060200A);
}
void E1000NetworkAdapter::out8(word address, byte data)
{
if (m_use_mmio) {
auto* ptr = (volatile byte*)(m_mmio_base.get() + address);
*ptr = data;
return;
}
IO::out8(m_io_base + address, data);
}
void E1000NetworkAdapter::out16(word address, word data)
{
if (m_use_mmio) {
auto* ptr = (volatile word*)(m_mmio_base.get() + address);
*ptr = data;
return;
}
IO::out16(m_io_base + address, data);
}
void E1000NetworkAdapter::out32(word address, dword data)
{
if (m_use_mmio) {
auto* ptr = (volatile dword*)(m_mmio_base.get() + address);
*ptr = data;
return;
}
IO::out32(m_io_base + address, data);
}
byte E1000NetworkAdapter::in8(word address)
{
if (m_use_mmio)
return *(volatile byte*)(m_mmio_base.get() + address);
return IO::in8(m_io_base + address);
}
word E1000NetworkAdapter::in16(word address)
{
if (m_use_mmio)
return *(volatile word*)(m_mmio_base.get() + address);
return IO::in16(m_io_base + address);
}
dword E1000NetworkAdapter::in32(word address)
{
if (m_use_mmio)
return *(volatile dword*)(m_mmio_base.get() + address);
return IO::in32(m_io_base + address);
}
void E1000NetworkAdapter::send_raw(const byte* data, int length)
{
dword tx_current = in32(REG_TXDESCTAIL);
#ifdef E1000_DEBUG
kprintf("E1000: Sending packet (%d bytes)\n", length);
#endif
auto& descriptor = m_tx_descriptors[tx_current];
ASSERT(length <= 8192);
memcpy((void*)descriptor.addr, data, length);
descriptor.length = length;
descriptor.status = 0;
descriptor.cmd = CMD_EOP | CMD_IFCS | CMD_RS;
#ifdef E1000_DEBUG
kprintf("E1000: Using tx descriptor %d (head is at %d)\n", tx_current, in32(REG_TXDESCHEAD));
#endif
tx_current = (tx_current + 1) % number_of_tx_descriptors;
out32(REG_TXDESCTAIL, tx_current);
while (!descriptor.status)
;
#ifdef E1000_DEBUG
kprintf("E1000: Sent packet, status is now %b!\n", descriptor.status);
#endif
}
void E1000NetworkAdapter::receive()
{
dword rx_current;
for (;;) {
rx_current = in32(REG_RXDESCTAIL);
if (rx_current == in32(REG_RXDESCHEAD))
return;
rx_current = (rx_current + 1) % number_of_rx_descriptors;
if (!(m_rx_descriptors[rx_current].status & 1))
break;
auto* buffer = (byte*)m_rx_descriptors[rx_current].addr;
word length = m_rx_descriptors[rx_current].length;
#ifdef E1000_DEBUG
kprintf("E1000: Received 1 packet @ %p (%u) bytes!\n", buffer, length);
#endif
did_receive(buffer, length);
m_rx_descriptors[rx_current].status = 0;
out32(REG_RXDESCTAIL, rx_current);
}
}

View file

@ -0,0 +1,74 @@
#pragma once
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/PCI.h>
#include <Kernel/MemoryManager.h>
#include <Kernel/IRQHandler.h>
#include <AK/OwnPtr.h>
class E1000NetworkAdapter final : public NetworkAdapter, public IRQHandler {
public:
static E1000NetworkAdapter* the();
static OwnPtr<E1000NetworkAdapter> autodetect();
E1000NetworkAdapter(PCI::Address, byte irq);
virtual ~E1000NetworkAdapter() override;
virtual void send_raw(const byte*, int) override;
private:
virtual void handle_irq() override;
virtual const char* class_name() const override { return "E1000NetworkAdapter"; }
struct [[gnu::packed]] e1000_rx_desc {
volatile uint64_t addr { 0 };
volatile uint16_t length { 0 };
volatile uint16_t checksum { 0 };
volatile uint8_t status { 0 };
volatile uint8_t errors { 0 };
volatile uint16_t special { 0 };
};
struct [[gnu::packed]] e1000_tx_desc {
volatile uint64_t addr { 0 };
volatile uint16_t length { 0 };
volatile uint8_t cso { 0 };
volatile uint8_t cmd { 0 };
volatile uint8_t status { 0 };
volatile uint8_t css { 0 };
volatile uint16_t special { 0 };
};
void detect_eeprom();
dword read_eeprom(byte address);
void read_mac_address();
void write_command(word address, dword);
dword read_command(word address);
void initialize_rx_descriptors();
void initialize_tx_descriptors();
void out8(word address, byte);
void out16(word address, word);
void out32(word address, dword);
byte in8(word address);
word in16(word address);
dword in32(word address);
void receive();
PCI::Address m_pci_address;
word m_io_base { 0 };
PhysicalAddress m_mmio_base;
byte m_interrupt_line { 0 };
bool m_has_eeprom { false };
bool m_use_mmio { false };
static const int number_of_rx_descriptors = 32;
static const int number_of_tx_descriptors = 8;
e1000_rx_desc* m_rx_descriptors;
e1000_tx_desc* m_tx_descriptors;
};

10
Kernel/Net/EtherType.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include <AK/Types.h>
struct EtherType {
enum : word {
ARP = 0x0806,
IPv4 = 0x0800,
};
};

View file

@ -0,0 +1,31 @@
#pragma once
#include <Kernel/Net/MACAddress.h>
#include <Kernel/NetworkOrdered.h>
class [[gnu::packed]] EthernetFrameHeader {
public:
EthernetFrameHeader() { }
~EthernetFrameHeader() { }
MACAddress destination() const { return m_destination; }
void set_destination(const MACAddress& address) { m_destination = address; }
MACAddress source() const { return m_source; }
void set_source(const MACAddress& address) { m_source = address; }
word ether_type() const { return m_ether_type; }
void set_ether_type(word ether_type) { m_ether_type = ether_type; }
const void* payload() const { return &m_payload[0]; }
void* payload() { return &m_payload[0]; }
private:
MACAddress m_destination;
MACAddress m_source;
NetworkOrdered<word> m_ether_type;
dword m_payload[0];
};
static_assert(sizeof(EthernetFrameHeader) == 14);

45
Kernel/Net/ICMP.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include <Kernel/Net/MACAddress.h>
#include <Kernel/Net/IPv4.h>
struct ICMPType {
enum {
EchoReply = 0,
EchoRequest = 8,
};
};
class [[gnu::packed]] ICMPHeader {
public:
ICMPHeader() { }
~ICMPHeader() { }
byte type() const { return m_type; }
void set_type(byte b) { m_type = b; }
byte code() const { return m_code; }
void set_code(byte b) { m_code = b; }
word checksum() const { return m_checksum; }
void set_checksum(word w) { m_checksum = w; }
const void* payload() const { return this + 1; }
void* payload() { return this + 1; }
private:
byte m_type { 0 };
byte m_code { 0 };
NetworkOrdered<word> m_checksum { 0 };
// NOTE: The rest of the header is 4 bytes
};
static_assert(sizeof(ICMPHeader) == 4);
struct [[gnu::packed]] ICMPEchoPacket {
ICMPHeader header;
NetworkOrdered<word> identifier;
NetworkOrdered<word> sequence_number;
void* payload() { return this + 1; }
const void* payload() const { return this + 1; }
};

135
Kernel/Net/IPv4.h Normal file
View file

@ -0,0 +1,135 @@
#pragma once
#include <AK/AKString.h>
#include <AK/Assertions.h>
#include <AK/Types.h>
#include <Kernel/NetworkOrdered.h>
enum class IPv4Protocol : word {
ICMP = 1,
TCP = 6,
UDP = 17,
};
NetworkOrdered<word> internet_checksum(const void*, size_t);
class [[gnu::packed]] IPv4Address {
public:
IPv4Address() { }
IPv4Address(const byte data[4])
{
m_data[0] = data[0];
m_data[1] = data[1];
m_data[2] = data[2];
m_data[3] = data[3];
}
IPv4Address(byte a, byte b, byte c, byte d)
{
m_data[0] = a;
m_data[1] = b;
m_data[2] = c;
m_data[3] = d;
}
byte operator[](int i) const
{
ASSERT(i >= 0 && i < 4);
return m_data[i];
}
String to_string() const
{
return String::format("%u.%u.%u.%u", m_data[0], m_data[1], m_data[2], m_data[3]);
}
bool operator==(const IPv4Address& other) const { return m_data_as_dword == other.m_data_as_dword; }
bool operator!=(const IPv4Address& other) const { return m_data_as_dword != other.m_data_as_dword; }
private:
union {
byte m_data[4];
dword m_data_as_dword;
};
};
static_assert(sizeof(IPv4Address) == 4);
namespace AK {
template<>
struct Traits<IPv4Address> {
static unsigned hash(const IPv4Address& address) { return string_hash((const char*)&address, sizeof(address)); }
static void dump(const IPv4Address& address) { kprintf("%s", address.to_string().characters()); }
};
}
class [[gnu::packed]] IPv4Packet {
public:
byte version() const { return (m_version_and_ihl >> 4) & 0xf; }
void set_version(byte version) { m_version_and_ihl = (m_version_and_ihl & 0x0f) | (version << 4); }
byte internet_header_length() const { return m_version_and_ihl & 0xf; }
void set_internet_header_length(byte ihl) { m_version_and_ihl = (m_version_and_ihl & 0xf0) | (ihl & 0x0f); }
word length() const { return m_length; }
void set_length(word length) { m_length = length; }
word ident() const { return m_ident; }
void set_ident(word ident) { m_ident = ident; }
byte ttl() const { return m_ttl; }
void set_ttl(byte ttl) { m_ttl = ttl; }
byte protocol() const { return m_protocol; }
void set_protocol(byte protocol) { m_protocol = protocol; }
word checksum() const { return m_checksum; }
void set_checksum(word checksum) { m_checksum = checksum; }
const IPv4Address& source() const { return m_source; }
void set_source(const IPv4Address& address) { m_source = address; }
const IPv4Address& destination() const { return m_destination; }
void set_destination(const IPv4Address& address) { m_destination = address; }
void* payload() { return this + 1; }
const void* payload() const { return this + 1; }
word payload_size() const { return m_length - sizeof(IPv4Packet); }
NetworkOrdered<word> compute_checksum() const
{
ASSERT(!m_checksum);
return internet_checksum(this, sizeof(IPv4Packet));
}
private:
byte m_version_and_ihl { 0 };
byte m_dscp_and_ecn { 0 };
NetworkOrdered<word> m_length;
NetworkOrdered<word> m_ident;
NetworkOrdered<word> m_flags_and_fragment;
byte m_ttl { 0 };
NetworkOrdered<byte> m_protocol;
NetworkOrdered<word> m_checksum;
IPv4Address m_source;
IPv4Address m_destination;
};
static_assert(sizeof(IPv4Packet) == 20);
inline NetworkOrdered<word> internet_checksum(const void* ptr, size_t count)
{
dword checksum = 0;
auto* w = (const word*)ptr;
while (count > 1) {
checksum += convert_between_host_and_network(*w++);
if (checksum & 0x80000000)
checksum = (checksum & 0xffff) | (checksum >> 16);
count -= 2;
}
while (checksum >> 16)
checksum = (checksum & 0xffff) + (checksum >> 16);
return ~checksum & 0xffff;
}

236
Kernel/Net/IPv4Socket.cpp Normal file
View file

@ -0,0 +1,236 @@
#include <Kernel/Net/IPv4Socket.h>
#include <Kernel/Net/TCPSocket.h>
#include <Kernel/Net/UDPSocket.h>
#include <Kernel/UnixTypes.h>
#include <Kernel/Process.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Net/IPv4.h>
#include <Kernel/Net/ICMP.h>
#include <Kernel/Net/TCP.h>
#include <Kernel/Net/UDP.h>
#include <Kernel/Net/ARP.h>
#include <Kernel/Net/Routing.h>
#include <LibC/errno_numbers.h>
#define IPV4_SOCKET_DEBUG
Lockable<HashTable<IPv4Socket*>>& IPv4Socket::all_sockets()
{
static Lockable<HashTable<IPv4Socket*>>* s_table;
if (!s_table)
s_table = new Lockable<HashTable<IPv4Socket*>>;
return *s_table;
}
Retained<IPv4Socket> IPv4Socket::create(int type, int protocol)
{
if (type == SOCK_STREAM)
return TCPSocket::create(protocol);
if (type == SOCK_DGRAM)
return UDPSocket::create(protocol);
return adopt(*new IPv4Socket(type, protocol));
}
IPv4Socket::IPv4Socket(int type, int protocol)
: Socket(AF_INET, type, protocol)
{
kprintf("%s(%u) IPv4Socket{%p} created with type=%u, protocol=%d\n", current->process().name().characters(), current->pid(), this, type, protocol);
LOCKER(all_sockets().lock());
all_sockets().resource().set(this);
}
IPv4Socket::~IPv4Socket()
{
LOCKER(all_sockets().lock());
all_sockets().resource().remove(this);
}
bool IPv4Socket::get_address(sockaddr* address, socklen_t* address_size)
{
// FIXME: Look into what fallback behavior we should have here.
if (*address_size != sizeof(sockaddr_in))
return false;
memcpy(address, &m_destination_address, sizeof(sockaddr_in));
*address_size = sizeof(sockaddr_in);
return true;
}
KResult IPv4Socket::bind(const sockaddr* address, socklen_t address_size)
{
ASSERT(!is_connected());
if (address_size != sizeof(sockaddr_in))
return KResult(-EINVAL);
if (address->sa_family != AF_INET)
return KResult(-EINVAL);
ASSERT_NOT_REACHED();
}
KResult IPv4Socket::connect(const sockaddr* address, socklen_t address_size)
{
ASSERT(!m_bound);
if (address_size != sizeof(sockaddr_in))
return KResult(-EINVAL);
if (address->sa_family != AF_INET)
return KResult(-EINVAL);
auto& ia = *(const sockaddr_in*)address;
m_destination_address = IPv4Address((const byte*)&ia.sin_addr.s_addr);
m_destination_port = ntohs(ia.sin_port);
return protocol_connect();
}
void IPv4Socket::attach_fd(SocketRole)
{
++m_attached_fds;
}
void IPv4Socket::detach_fd(SocketRole)
{
--m_attached_fds;
}
bool IPv4Socket::can_read(SocketRole) const
{
if (protocol_is_disconnected())
return true;
return m_can_read;
}
ssize_t IPv4Socket::read(SocketRole, byte* buffer, ssize_t size)
{
return recvfrom(buffer, size, 0, nullptr, 0);
}
ssize_t IPv4Socket::write(SocketRole, const byte* data, ssize_t size)
{
return sendto(data, size, 0, nullptr, 0);
}
bool IPv4Socket::can_write(SocketRole) const
{
return true;
}
int IPv4Socket::allocate_source_port_if_needed()
{
if (m_source_port)
return m_source_port;
int port = protocol_allocate_source_port();
if (port < 0)
return port;
m_source_port = (word)port;
return port;
}
ssize_t IPv4Socket::sendto(const void* data, size_t data_length, int flags, const sockaddr* addr, socklen_t addr_length)
{
(void)flags;
if (addr && addr_length != sizeof(sockaddr_in))
return -EINVAL;
if (addr) {
if (addr->sa_family != AF_INET) {
kprintf("sendto: Bad address family: %u is not AF_INET!\n", addr->sa_family);
return -EAFNOSUPPORT;
}
auto& ia = *(const sockaddr_in*)addr;
m_destination_address = IPv4Address((const byte*)&ia.sin_addr.s_addr);
m_destination_port = ntohs(ia.sin_port);
}
auto* adapter = adapter_for_route_to(m_destination_address);
if (!adapter)
return -EHOSTUNREACH;
int rc = allocate_source_port_if_needed();
if (rc < 0)
return rc;
kprintf("sendto: destination=%s:%u\n", m_destination_address.to_string().characters(), m_destination_port);
if (type() == SOCK_RAW) {
adapter->send_ipv4(MACAddress(), m_destination_address, (IPv4Protocol)protocol(), ByteBuffer::copy(data, data_length));
return data_length;
}
return protocol_send(data, data_length);
}
ssize_t IPv4Socket::recvfrom(void* buffer, size_t buffer_length, int flags, sockaddr* addr, socklen_t* addr_length)
{
(void)flags;
if (addr_length && *addr_length < sizeof(sockaddr_in))
return -EINVAL;
#ifdef IPV4_SOCKET_DEBUG
kprintf("recvfrom: type=%d, source_port=%u\n", type(), source_port());
#endif
ByteBuffer packet_buffer;
{
LOCKER(lock());
if (!m_receive_queue.is_empty()) {
packet_buffer = m_receive_queue.take_first();
m_can_read = !m_receive_queue.is_empty();
#ifdef IPV4_SOCKET_DEBUG
kprintf("IPv4Socket(%p): recvfrom without blocking %d bytes, packets in queue: %d\n", this, packet_buffer.size(), m_receive_queue.size_slow());
#endif
}
}
if (packet_buffer.is_null()) {
if (protocol_is_disconnected()) {
kprintf("IPv4Socket{%p} is protocol-disconnected, returning 0 in recvfrom!\n", this);
return 0;
}
current->set_blocked_socket(this);
load_receive_deadline();
current->block(Thread::BlockedReceive);
LOCKER(lock());
if (!m_can_read) {
// Unblocked due to timeout.
return -EAGAIN;
}
ASSERT(m_can_read);
ASSERT(!m_receive_queue.is_empty());
packet_buffer = m_receive_queue.take_first();
m_can_read = !m_receive_queue.is_empty();
#ifdef IPV4_SOCKET_DEBUG
kprintf("IPv4Socket(%p): recvfrom with blocking %d bytes, packets in queue: %d\n", this, packet_buffer.size(), m_receive_queue.size_slow());
#endif
}
ASSERT(!packet_buffer.is_null());
auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.pointer());
if (addr) {
auto& ia = *(sockaddr_in*)addr;
memcpy(&ia.sin_addr, &m_destination_address, sizeof(IPv4Address));
ia.sin_family = AF_INET;
ASSERT(addr_length);
*addr_length = sizeof(sockaddr_in);
}
if (type() == SOCK_RAW) {
ASSERT(buffer_length >= ipv4_packet.payload_size());
memcpy(buffer, ipv4_packet.payload(), ipv4_packet.payload_size());
return ipv4_packet.payload_size();
}
return protocol_receive(packet_buffer, buffer, buffer_length, flags, addr, addr_length);
}
void IPv4Socket::did_receive(ByteBuffer&& packet)
{
LOCKER(lock());
auto packet_size = packet.size();
m_receive_queue.append(move(packet));
m_can_read = true;
m_bytes_received += packet_size;
#ifdef IPV4_SOCKET_DEBUG
kprintf("IPv4Socket(%p): did_receive %d bytes, total_received=%u, packets in queue: %d\n", this, packet_size, m_bytes_received, m_receive_queue.size_slow());
#endif
}

98
Kernel/Net/IPv4Socket.h Normal file
View file

@ -0,0 +1,98 @@
#pragma once
#include <Kernel/Socket.h>
#include <Kernel/DoubleBuffer.h>
#include <Kernel/Net/IPv4.h>
#include <AK/HashMap.h>
#include <Kernel/Lock.h>
#include <AK/SinglyLinkedList.h>
class IPv4SocketHandle;
class TCPSocketHandle;
class NetworkAdapter;
class TCPPacket;
class TCPSocket;
class IPv4Socket : public Socket {
public:
static Retained<IPv4Socket> create(int type, int protocol);
virtual ~IPv4Socket() override;
static Lockable<HashTable<IPv4Socket*>>& all_sockets();
virtual KResult bind(const sockaddr*, socklen_t) override;
virtual KResult connect(const sockaddr*, socklen_t) override;
virtual bool get_address(sockaddr*, socklen_t*) override;
virtual void attach_fd(SocketRole) override;
virtual void detach_fd(SocketRole) override;
virtual bool can_read(SocketRole) const override;
virtual ssize_t read(SocketRole, byte*, ssize_t) override;
virtual ssize_t write(SocketRole, const byte*, ssize_t) override;
virtual bool can_write(SocketRole) const override;
virtual ssize_t sendto(const void*, size_t, int, const sockaddr*, socklen_t) override;
virtual ssize_t recvfrom(void*, size_t, int flags, sockaddr*, socklen_t*) override;
void did_receive(ByteBuffer&&);
const IPv4Address& source_address() const;
word source_port() const { return m_source_port; }
void set_source_port(word port) { m_source_port = port; }
const IPv4Address& destination_address() const { return m_destination_address; }
word destination_port() const { return m_destination_port; }
void set_destination_port(word port) { m_destination_port = port; }
protected:
IPv4Socket(int type, int protocol);
int allocate_source_port_if_needed();
virtual int protocol_receive(const ByteBuffer&, void*, size_t, int, sockaddr*, socklen_t*) { return -ENOTIMPL; }
virtual int protocol_send(const void*, int) { return -ENOTIMPL; }
virtual KResult protocol_connect() { return KSuccess; }
virtual int protocol_allocate_source_port() { return 0; }
virtual bool protocol_is_disconnected() const { return false; }
private:
virtual bool is_ipv4() const override { return true; }
bool m_bound { false };
int m_attached_fds { 0 };
IPv4Address m_destination_address;
DoubleBuffer m_for_client;
DoubleBuffer m_for_server;
SinglyLinkedList<ByteBuffer> m_receive_queue;
word m_source_port { 0 };
word m_destination_port { 0 };
dword m_bytes_received { 0 };
bool m_can_read { false };
};
class IPv4SocketHandle : public SocketHandle {
public:
IPv4SocketHandle() { }
IPv4SocketHandle(RetainPtr<IPv4Socket>&& socket)
: SocketHandle(move(socket))
{
}
IPv4SocketHandle(IPv4SocketHandle&& other)
: SocketHandle(move(other))
{
}
IPv4SocketHandle(const IPv4SocketHandle&) = delete;
IPv4SocketHandle& operator=(const IPv4SocketHandle&) = delete;
IPv4Socket* operator->() { return &socket(); }
const IPv4Socket* operator->() const { return &socket(); }
IPv4Socket& socket() { return static_cast<IPv4Socket&>(SocketHandle::socket()); }
const IPv4Socket& socket() const { return static_cast<const IPv4Socket&>(SocketHandle::socket()); }
};

View file

@ -1,6 +1,6 @@
#pragma once
#include <Kernel/NetworkAdapter.h>
#include <Kernel/Net/NetworkAdapter.h>
class LoopbackAdapter final : public NetworkAdapter {
public:

47
Kernel/Net/MACAddress.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/AKString.h>
#include <AK/Types.h>
#include <Kernel/StdLib.h>
class [[gnu::packed]] MACAddress {
public:
MACAddress() { }
MACAddress(const byte data[6])
{
memcpy(m_data, data, 6);
}
~MACAddress() { }
byte operator[](int i) const
{
ASSERT(i >= 0 && i < 6);
return m_data[i];
}
bool operator==(const MACAddress& other) const
{
return !memcmp(m_data, other.m_data, sizeof(m_data));
}
String to_string() const
{
return String::format("%b:%b:%b:%b:%b:%b", m_data[0], m_data[1], m_data[2], m_data[3], m_data[4], m_data[5]);
}
private:
byte m_data[6];
};
static_assert(sizeof(MACAddress) == 6);
namespace AK {
template<>
struct Traits<MACAddress> {
static unsigned hash(const MACAddress& address) { return string_hash((const char*)&address, sizeof(address)); }
static void dump(const MACAddress& address) { kprintf("%s", address.to_string().characters()); }
};
}

View file

@ -0,0 +1,96 @@
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Net/EthernetFrameHeader.h>
#include <Kernel/Net/EtherType.h>
#include <Kernel/StdLib.h>
#include <Kernel/kmalloc.h>
#include <AK/HashTable.h>
#include <Kernel/Lock.h>
static Lockable<HashTable<NetworkAdapter*>>& all_adapters()
{
static Lockable<HashTable<NetworkAdapter*>>* table;
if (!table)
table = new Lockable<HashTable<NetworkAdapter*>>;
return *table;
}
NetworkAdapter* NetworkAdapter::from_ipv4_address(const IPv4Address& address)
{
LOCKER(all_adapters().lock());
for (auto* adapter : all_adapters().resource()) {
if (adapter->ipv4_address() == address)
return adapter;
}
return nullptr;
}
NetworkAdapter::NetworkAdapter()
: m_packet_queue_alarm(*this)
{
// FIXME: I wanna lock :(
all_adapters().resource().set(this);
}
NetworkAdapter::~NetworkAdapter()
{
// FIXME: I wanna lock :(
all_adapters().resource().remove(this);
}
void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet)
{
int size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(ARPPacket);
auto buffer = ByteBuffer::create_zeroed(size_in_bytes);
auto* eth = (EthernetFrameHeader*)buffer.pointer();
eth->set_source(mac_address());
eth->set_destination(destination);
eth->set_ether_type(EtherType::ARP);
memcpy(eth->payload(), &packet, sizeof(ARPPacket));
send_raw((byte*)eth, size_in_bytes);
}
void NetworkAdapter::send_ipv4(const MACAddress& destination_mac, const IPv4Address& destination_ipv4, IPv4Protocol protocol, ByteBuffer&& payload)
{
size_t size_in_bytes = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet) + payload.size();
auto buffer = ByteBuffer::create_zeroed(size_in_bytes);
auto& eth = *(EthernetFrameHeader*)buffer.pointer();
eth.set_source(mac_address());
eth.set_destination(destination_mac);
eth.set_ether_type(EtherType::IPv4);
auto& ipv4 = *(IPv4Packet*)eth.payload();
ipv4.set_version(4);
ipv4.set_internet_header_length(5);
ipv4.set_source(ipv4_address());
ipv4.set_destination(destination_ipv4);
ipv4.set_protocol((byte)protocol);
ipv4.set_length(sizeof(IPv4Packet) + payload.size());
ipv4.set_ident(1);
ipv4.set_ttl(64);
ipv4.set_checksum(ipv4.compute_checksum());
memcpy(ipv4.payload(), payload.pointer(), payload.size());
send_raw((const byte*)&eth, size_in_bytes);
}
void NetworkAdapter::did_receive(const byte* data, int length)
{
InterruptDisabler disabler;
m_packet_queue.append(ByteBuffer::copy(data, length));
}
ByteBuffer NetworkAdapter::dequeue_packet()
{
InterruptDisabler disabler;
if (m_packet_queue.is_empty())
return { };
return m_packet_queue.take_first();
}
void NetworkAdapter::set_ipv4_address(const IPv4Address& address)
{
m_ipv4_address = address;
}
bool PacketQueueAlarm::is_ringing() const
{
return m_adapter.has_queued_packets();
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/SinglyLinkedList.h>
#include <AK/Types.h>
#include <Kernel/Net/MACAddress.h>
#include <Kernel/Net/IPv4.h>
#include <Kernel/Net/ARP.h>
#include <Kernel/Net/ICMP.h>
#include <Kernel/Alarm.h>
class NetworkAdapter;
class PacketQueueAlarm final : public Alarm {
public:
PacketQueueAlarm(NetworkAdapter& adapter) : m_adapter(adapter) { }
virtual ~PacketQueueAlarm() override { }
virtual bool is_ringing() const override;
private:
NetworkAdapter& m_adapter;
};
class NetworkAdapter {
public:
static NetworkAdapter* from_ipv4_address(const IPv4Address&);
virtual ~NetworkAdapter();
virtual const char* class_name() const = 0;
MACAddress mac_address() { return m_mac_address; }
IPv4Address ipv4_address() const { return m_ipv4_address; }
void set_ipv4_address(const IPv4Address&);
void send(const MACAddress&, const ARPPacket&);
void send_ipv4(const MACAddress&, const IPv4Address&, IPv4Protocol, ByteBuffer&& payload);
ByteBuffer dequeue_packet();
Alarm& packet_queue_alarm() { return m_packet_queue_alarm; }
bool has_queued_packets() const { return !m_packet_queue.is_empty(); }
protected:
NetworkAdapter();
void set_mac_address(const MACAddress& mac_address) { m_mac_address = mac_address; }
virtual void send_raw(const byte*, int) = 0;
void did_receive(const byte*, int);
private:
MACAddress m_mac_address;
IPv4Address m_ipv4_address;
PacketQueueAlarm m_packet_queue_alarm;
SinglyLinkedList<ByteBuffer> m_packet_queue;
};

340
Kernel/Net/NetworkTask.cpp Normal file
View file

@ -0,0 +1,340 @@
#include <Kernel/Net/E1000NetworkAdapter.h>
#include <Kernel/Net/EthernetFrameHeader.h>
#include <Kernel/Net/ARP.h>
#include <Kernel/Net/ICMP.h>
#include <Kernel/Net/UDP.h>
#include <Kernel/Net/TCP.h>
#include <Kernel/Net/IPv4.h>
#include <Kernel/Net/IPv4Socket.h>
#include <Kernel/Net/TCPSocket.h>
#include <Kernel/Net/UDPSocket.h>
#include <Kernel/Net/LoopbackAdapter.h>
#include <Kernel/Process.h>
#include <Kernel/Net/EtherType.h>
#include <Kernel/Lock.h>
//#define ETHERNET_DEBUG
#define IPV4_DEBUG
//#define ICMP_DEBUG
#define UDP_DEBUG
#define TCP_DEBUG
static void handle_arp(const EthernetFrameHeader&, int frame_size);
static void handle_ipv4(const EthernetFrameHeader&, int frame_size);
static void handle_icmp(const EthernetFrameHeader&, int frame_size);
static void handle_udp(const EthernetFrameHeader&, int frame_size);
static void handle_tcp(const EthernetFrameHeader&, int frame_size);
Lockable<HashMap<IPv4Address, MACAddress>>& arp_table()
{
static Lockable<HashMap<IPv4Address, MACAddress>>* the;
if (!the)
the = new Lockable<HashMap<IPv4Address, MACAddress>>;
return *the;
}
void NetworkTask_main()
{
LoopbackAdapter::the();
auto* adapter_ptr = E1000NetworkAdapter::the();
ASSERT(adapter_ptr);
auto& adapter = *adapter_ptr;
adapter.set_ipv4_address(IPv4Address(192, 168, 5, 2));
auto dequeue_packet = [&] () -> ByteBuffer {
if (LoopbackAdapter::the().has_queued_packets())
return LoopbackAdapter::the().dequeue_packet();
if (adapter.has_queued_packets())
return adapter.dequeue_packet();
return { };
};
kprintf("NetworkTask: Enter main loop.\n");
for (;;) {
auto packet = dequeue_packet();
if (packet.is_null()) {
// FIXME: Wake up when one of the adapters has packets.
current->sleep(1);
continue;
}
if (packet.size() < (int)(sizeof(EthernetFrameHeader))) {
kprintf("NetworkTask: Packet is too small to be an Ethernet packet! (%d)\n", packet.size());
continue;
}
auto& eth = *(const EthernetFrameHeader*)packet.pointer();
#ifdef ETHERNET_DEBUG
kprintf("NetworkTask: From %s to %s, ether_type=%w, packet_length=%u\n",
eth.source().to_string().characters(),
eth.destination().to_string().characters(),
eth.ether_type(),
packet.size()
);
#endif
switch (eth.ether_type()) {
case EtherType::ARP:
handle_arp(eth, packet.size());
break;
case EtherType::IPv4:
handle_ipv4(eth, packet.size());
break;
}
}
}
void handle_arp(const EthernetFrameHeader& eth, int frame_size)
{
constexpr int minimum_arp_frame_size = sizeof(EthernetFrameHeader) + sizeof(ARPPacket);
if (frame_size < minimum_arp_frame_size) {
kprintf("handle_arp: Frame too small (%d, need %d)\n", frame_size, minimum_arp_frame_size);
return;
}
auto& packet = *static_cast<const ARPPacket*>(eth.payload());
if (packet.hardware_type() != 1 || packet.hardware_address_length() != sizeof(MACAddress)) {
kprintf("handle_arp: Hardware type not ethernet (%w, len=%u)\n",
packet.hardware_type(),
packet.hardware_address_length()
);
return;
}
if (packet.protocol_type() != EtherType::IPv4 || packet.protocol_address_length() != sizeof(IPv4Address)) {
kprintf("handle_arp: Protocol type not IPv4 (%w, len=%u)\n",
packet.hardware_type(),
packet.protocol_address_length()
);
return;
}
#ifdef ARP_DEBUG
kprintf("handle_arp: operation=%w, sender=%s/%s, target=%s/%s\n",
packet.operation(),
packet.sender_hardware_address().to_string().characters(),
packet.sender_protocol_address().to_string().characters(),
packet.target_hardware_address().to_string().characters(),
packet.target_protocol_address().to_string().characters()
);
#endif
if (packet.operation() == ARPOperation::Request) {
// Who has this IP address?
if (auto* adapter = NetworkAdapter::from_ipv4_address(packet.target_protocol_address())) {
// We do!
kprintf("handle_arp: Responding to ARP request for my IPv4 address (%s)\n",
adapter->ipv4_address().to_string().characters());
ARPPacket response;
response.set_operation(ARPOperation::Response);
response.set_target_hardware_address(packet.sender_hardware_address());
response.set_target_protocol_address(packet.sender_protocol_address());
response.set_sender_hardware_address(adapter->mac_address());
response.set_sender_protocol_address(adapter->ipv4_address());
adapter->send(packet.sender_hardware_address(), response);
}
return;
}
if (packet.operation() == ARPOperation::Response) {
// Someone has this IPv4 address. I guess we can try to remember that.
// FIXME: Protect against ARP spamming.
// FIXME: Support static ARP table entries.
LOCKER(arp_table().lock());
arp_table().resource().set(packet.sender_protocol_address(), packet.sender_hardware_address());
kprintf("ARP table (%d entries):\n", arp_table().resource().size());
for (auto& it : arp_table().resource()) {
kprintf("%s :: %s\n", it.value.to_string().characters(), it.key.to_string().characters());
}
}
}
void handle_ipv4(const EthernetFrameHeader& eth, int frame_size)
{
constexpr int minimum_ipv4_frame_size = sizeof(EthernetFrameHeader) + sizeof(IPv4Packet);
if (frame_size < minimum_ipv4_frame_size) {
kprintf("handle_ipv4: Frame too small (%d, need %d)\n", frame_size, minimum_ipv4_frame_size);
return;
}
auto& packet = *static_cast<const IPv4Packet*>(eth.payload());
#ifdef IPV4_DEBUG
kprintf("handle_ipv4: source=%s, target=%s\n",
packet.source().to_string().characters(),
packet.destination().to_string().characters()
);
#endif
switch ((IPv4Protocol)packet.protocol()) {
case IPv4Protocol::ICMP:
return handle_icmp(eth, frame_size);
case IPv4Protocol::UDP:
return handle_udp(eth, frame_size);
case IPv4Protocol::TCP:
return handle_tcp(eth, frame_size);
default:
kprintf("handle_ipv4: Unhandled protocol %u\n", packet.protocol());
break;
}
}
void handle_icmp(const EthernetFrameHeader& eth, int frame_size)
{
(void)frame_size;
auto& ipv4_packet = *static_cast<const IPv4Packet*>(eth.payload());
auto& icmp_header = *static_cast<const ICMPHeader*>(ipv4_packet.payload());
#ifdef ICMP_DEBUG
kprintf("handle_icmp: source=%s, destination=%s, type=%b, code=%b\n",
ipv4_packet.source().to_string().characters(),
ipv4_packet.destination().to_string().characters(),
icmp_header.type(),
icmp_header.code()
);
#endif
{
LOCKER(IPv4Socket::all_sockets().lock());
for (RetainPtr<IPv4Socket> socket : IPv4Socket::all_sockets().resource()) {
LOCKER(socket->lock());
if (socket->protocol() != (unsigned)IPv4Protocol::ICMP)
continue;
socket->did_receive(ByteBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
}
}
auto* adapter = NetworkAdapter::from_ipv4_address(ipv4_packet.destination());
if (!adapter)
return;
if (icmp_header.type() == ICMPType::EchoRequest) {
auto& request = reinterpret_cast<const ICMPEchoPacket&>(icmp_header);
kprintf("handle_icmp: EchoRequest from %s: id=%u, seq=%u\n",
ipv4_packet.source().to_string().characters(),
(word)request.identifier,
(word)request.sequence_number
);
size_t icmp_packet_size = ipv4_packet.payload_size();
auto buffer = ByteBuffer::create_zeroed(icmp_packet_size);
auto& response = *(ICMPEchoPacket*)buffer.pointer();
response.header.set_type(ICMPType::EchoReply);
response.header.set_code(0);
response.identifier = request.identifier;
response.sequence_number = request.sequence_number;
if (size_t icmp_payload_size = icmp_packet_size - sizeof(ICMPEchoPacket))
memcpy(response.payload(), request.payload(), icmp_payload_size);
response.header.set_checksum(internet_checksum(&response, icmp_packet_size));
adapter->send_ipv4(eth.source(), ipv4_packet.source(), IPv4Protocol::ICMP, move(buffer));
}
}
void handle_udp(const EthernetFrameHeader& eth, int frame_size)
{
(void)frame_size;
auto& ipv4_packet = *static_cast<const IPv4Packet*>(eth.payload());
auto* adapter = NetworkAdapter::from_ipv4_address(ipv4_packet.destination());
if (!adapter) {
kprintf("handle_udp: this packet is not for me, it's for %s\n", ipv4_packet.destination().to_string().characters());
return;
}
auto& udp_packet = *static_cast<const UDPPacket*>(ipv4_packet.payload());
#ifdef UDP_DEBUG
kprintf("handle_udp: source=%s:%u, destination=%s:%u length=%u\n",
ipv4_packet.source().to_string().characters(),
udp_packet.source_port(),
ipv4_packet.destination().to_string().characters(),
udp_packet.destination_port(),
udp_packet.length()
);
#endif
auto socket = UDPSocket::from_port(udp_packet.destination_port());
if (!socket) {
kprintf("handle_udp: No UDP socket for port %u\n", udp_packet.destination_port());
return;
}
ASSERT(socket->type() == SOCK_DGRAM);
ASSERT(socket->source_port() == udp_packet.destination_port());
socket->did_receive(ByteBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
}
void handle_tcp(const EthernetFrameHeader& eth, int frame_size)
{
(void)frame_size;
auto& ipv4_packet = *static_cast<const IPv4Packet*>(eth.payload());
auto* adapter = NetworkAdapter::from_ipv4_address(ipv4_packet.destination());
if (!adapter) {
kprintf("handle_tcp: this packet is not for me, it's for %s\n", ipv4_packet.destination().to_string().characters());
return;
}
auto& tcp_packet = *static_cast<const TCPPacket*>(ipv4_packet.payload());
size_t payload_size = ipv4_packet.payload_size() - tcp_packet.header_size();
#ifdef TCP_DEBUG
kprintf("handle_tcp: source=%s:%u, destination=%s:%u seq_no=%u, ack_no=%u, flags=%w (%s %s), window_size=%u, payload_size=%u\n",
ipv4_packet.source().to_string().characters(),
tcp_packet.source_port(),
ipv4_packet.destination().to_string().characters(),
tcp_packet.destination_port(),
tcp_packet.sequence_number(),
tcp_packet.ack_number(),
tcp_packet.flags(),
tcp_packet.has_syn() ? "SYN" : "",
tcp_packet.has_ack() ? "ACK" : "",
tcp_packet.window_size(),
payload_size
);
#endif
auto socket = TCPSocket::from_port(tcp_packet.destination_port());
if (!socket) {
kprintf("handle_tcp: No TCP socket for port %u\n", tcp_packet.destination_port());
return;
}
ASSERT(socket->type() == SOCK_STREAM);
ASSERT(socket->source_port() == tcp_packet.destination_port());
if (tcp_packet.ack_number() != socket->sequence_number()) {
kprintf("handle_tcp: ack/seq mismatch: got %u, wanted %u\n", tcp_packet.ack_number(), socket->sequence_number());
return;
}
if (tcp_packet.has_syn() && tcp_packet.has_ack()) {
socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1);
socket->send_tcp_packet(TCPFlags::ACK);
socket->set_connected(true);
kprintf("handle_tcp: Connection established!\n");
socket->set_state(TCPSocket::State::Connected);
return;
}
if (tcp_packet.has_fin()) {
kprintf("handle_tcp: Got FIN, payload_size=%u\n", payload_size);
if (payload_size != 0)
socket->did_receive(ByteBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
socket->set_ack_number(tcp_packet.sequence_number() + payload_size + 1);
socket->send_tcp_packet(TCPFlags::FIN | TCPFlags::ACK);
socket->set_state(TCPSocket::State::Disconnecting);
return;
}
socket->set_ack_number(tcp_packet.sequence_number() + payload_size);
kprintf("Got packet with ack_no=%u, seq_no=%u, payload_size=%u, acking it with new ack_no=%u, seq_no=%u\n",
tcp_packet.ack_number(),
tcp_packet.sequence_number(),
payload_size,
socket->ack_number(),
socket->sequence_number()
);
socket->send_tcp_packet(TCPFlags::ACK);
if (payload_size != 0)
socket->did_receive(ByteBuffer::copy(&ipv4_packet, sizeof(IPv4Packet) + ipv4_packet.payload_size()));
}

3
Kernel/Net/NetworkTask.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once
void NetworkTask_main();

View file

@ -1,5 +1,5 @@
#pragma once
#include <Kernel/NetworkAdapter.h>
#include <Kernel/Net/NetworkAdapter.h>
NetworkAdapter* adapter_for_route_to(const IPv4Address&);

69
Kernel/Net/TCP.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#include <Kernel/Net/IPv4.h>
struct TCPFlags {
enum : word {
FIN = 0x01,
SYN = 0x02,
RST = 0x04,
PUSH = 0x08,
ACK = 0x10,
URG = 0x20
};
};
class [[gnu::packed]] TCPPacket {
public:
TCPPacket() { }
~TCPPacket() { }
size_t header_size() const { return data_offset() * sizeof(dword); }
word source_port() const { return m_source_port; }
void set_source_port(word port) { m_source_port = port; }
word destination_port() const { return m_destination_port; }
void set_destination_port(word port) { m_destination_port = port; }
dword sequence_number() const { return m_sequence_number; }
void set_sequence_number(dword number) { m_sequence_number = number; }
dword ack_number() const { return m_ack_number; }
void set_ack_number(dword number) { m_ack_number = number; }
word flags() const { return m_flags_and_data_offset & 0x1ff; }
void set_flags(word flags) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0x1ff) | (flags & 0x1ff); }
bool has_syn() const { return flags() & TCPFlags::SYN; }
bool has_ack() const { return flags() & TCPFlags::ACK; }
bool has_fin() const { return flags() & TCPFlags::FIN; }
byte data_offset() const { return (m_flags_and_data_offset & 0xf000) >> 12; }
void set_data_offset(word data_offset) { m_flags_and_data_offset = (m_flags_and_data_offset & ~0xf000) | data_offset << 12; }
word window_size() const { return m_window_size; }
void set_window_size(word window_size) { m_window_size = window_size; }
word checksum() const { return m_checksum; }
void set_checksum(word checksum) { m_checksum = checksum; }
word urgent() const { return m_urgent; }
void set_urgent(word urgent) { m_urgent = urgent; }
const void* payload() const { return ((const byte*)this) + header_size(); }
void* payload() { return ((byte*)this) + header_size(); }
private:
NetworkOrdered<word> m_source_port;
NetworkOrdered<word> m_destination_port;
NetworkOrdered<dword> m_sequence_number;
NetworkOrdered<dword> m_ack_number;
NetworkOrdered<word> m_flags_and_data_offset;
NetworkOrdered<word> m_window_size;
NetworkOrdered<word> m_checksum;
NetworkOrdered<word> m_urgent;
};
static_assert(sizeof(TCPPacket) == 20);

203
Kernel/Net/TCPSocket.cpp Normal file
View file

@ -0,0 +1,203 @@
#include <Kernel/Net/TCPSocket.h>
#include <Kernel/Net/TCP.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Net/Routing.h>
#include <Kernel/Process.h>
#include <Kernel/RandomDevice.h>
Lockable<HashMap<word, TCPSocket*>>& TCPSocket::sockets_by_port()
{
static Lockable<HashMap<word, TCPSocket*>>* s_map;
if (!s_map)
s_map = new Lockable<HashMap<word, TCPSocket*>>;
return *s_map;
}
TCPSocketHandle TCPSocket::from_port(word port)
{
RetainPtr<TCPSocket> socket;
{
LOCKER(sockets_by_port().lock());
auto it = sockets_by_port().resource().find(port);
if (it == sockets_by_port().resource().end())
return { };
socket = (*it).value;
ASSERT(socket);
}
return { move(socket) };
}
TCPSocket::TCPSocket(int protocol)
: IPv4Socket(SOCK_STREAM, protocol)
{
}
TCPSocket::~TCPSocket()
{
LOCKER(sockets_by_port().lock());
sockets_by_port().resource().remove(source_port());
}
Retained<TCPSocket> TCPSocket::create(int protocol)
{
return adopt(*new TCPSocket(protocol));
}
int TCPSocket::protocol_receive(const ByteBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length)
{
(void)flags;
(void)addr_length;
ASSERT(!packet_buffer.is_null());
auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.pointer());
auto& tcp_packet = *static_cast<const TCPPacket*>(ipv4_packet.payload());
size_t payload_size = packet_buffer.size() - sizeof(IPv4Packet) - tcp_packet.header_size();
kprintf("payload_size %u, will it fit in %u?\n", payload_size, buffer_size);
ASSERT(buffer_size >= payload_size);
if (addr) {
auto& ia = *(sockaddr_in*)addr;
ia.sin_port = htons(tcp_packet.destination_port());
}
memcpy(buffer, tcp_packet.payload(), payload_size);
return payload_size;
}
int TCPSocket::protocol_send(const void* data, int data_length)
{
auto* adapter = adapter_for_route_to(destination_address());
if (!adapter)
return -EHOSTUNREACH;
send_tcp_packet(TCPFlags::PUSH | TCPFlags::ACK, data, data_length);
return data_length;
}
void TCPSocket::send_tcp_packet(word flags, const void* payload, int payload_size)
{
// FIXME: Maybe the socket should be bound to an adapter instead of looking it up every time?
auto* adapter = adapter_for_route_to(destination_address());
ASSERT(adapter);
auto buffer = ByteBuffer::create_zeroed(sizeof(TCPPacket) + payload_size);
auto& tcp_packet = *(TCPPacket*)(buffer.pointer());
ASSERT(source_port());
tcp_packet.set_source_port(source_port());
tcp_packet.set_destination_port(destination_port());
tcp_packet.set_window_size(1024);
tcp_packet.set_sequence_number(m_sequence_number);
tcp_packet.set_data_offset(sizeof(TCPPacket) / sizeof(dword));
tcp_packet.set_flags(flags);
if (flags & TCPFlags::ACK)
tcp_packet.set_ack_number(m_ack_number);
if (flags == TCPFlags::SYN) {
++m_sequence_number;
} else {
m_sequence_number += payload_size;
}
memcpy(tcp_packet.payload(), payload, payload_size);
tcp_packet.set_checksum(compute_tcp_checksum(adapter->ipv4_address(), destination_address(), tcp_packet, payload_size));
kprintf("sending tcp packet from %s:%u to %s:%u with (%s %s) seq_no=%u, ack_no=%u\n",
adapter->ipv4_address().to_string().characters(),
source_port(),
destination_address().to_string().characters(),
destination_port(),
tcp_packet.has_syn() ? "SYN" : "",
tcp_packet.has_ack() ? "ACK" : "",
tcp_packet.sequence_number(),
tcp_packet.ack_number()
);
adapter->send_ipv4(MACAddress(), destination_address(), IPv4Protocol::TCP, move(buffer));
}
NetworkOrdered<word> TCPSocket::compute_tcp_checksum(const IPv4Address& source, const IPv4Address& destination, const TCPPacket& packet, word payload_size)
{
struct [[gnu::packed]] PseudoHeader {
IPv4Address source;
IPv4Address destination;
byte zero;
byte protocol;
NetworkOrdered<word> payload_size;
};
PseudoHeader pseudo_header { source, destination, 0, (byte)IPv4Protocol::TCP, sizeof(TCPPacket) + payload_size };
dword checksum = 0;
auto* w = (const NetworkOrdered<word>*)&pseudo_header;
for (size_t i = 0; i < sizeof(pseudo_header) / sizeof(word); ++i) {
checksum += w[i];
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
w = (const NetworkOrdered<word>*)&packet;
for (size_t i = 0; i < sizeof(packet) / sizeof(word); ++i) {
checksum += w[i];
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
ASSERT(packet.data_offset() * 4 == sizeof(TCPPacket));
w = (const NetworkOrdered<word>*)packet.payload();
for (size_t i = 0; i < payload_size / sizeof(word); ++i) {
checksum += w[i];
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
if (payload_size & 1) {
word expanded_byte = ((const byte*)packet.payload())[payload_size - 1] << 8;
checksum += expanded_byte;
if (checksum > 0xffff)
checksum = (checksum >> 16) + (checksum & 0xffff);
}
return ~(checksum & 0xffff);
}
KResult TCPSocket::protocol_connect()
{
auto* adapter = adapter_for_route_to(destination_address());
if (!adapter)
return KResult(-EHOSTUNREACH);
allocate_source_port_if_needed();
m_sequence_number = 0;
m_ack_number = 0;
send_tcp_packet(TCPFlags::SYN);
m_state = State::Connecting;
current->set_blocked_socket(this);
current->block(Thread::BlockedConnect);
ASSERT(is_connected());
return KSuccess;
}
int TCPSocket::protocol_allocate_source_port()
{
static const word first_ephemeral_port = 32768;
static const word last_ephemeral_port = 60999;
static const word ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port;
word first_scan_port = first_ephemeral_port + (word)(RandomDevice::random_percentage() * ephemeral_port_range_size);
LOCKER(sockets_by_port().lock());
for (word port = first_scan_port;;) {
auto it = sockets_by_port().resource().find(port);
if (it == sockets_by_port().resource().end()) {
set_source_port(port);
sockets_by_port().resource().set(port, this);
return port;
}
++port;
if (port > last_ephemeral_port)
port = first_ephemeral_port;
if (port == first_scan_port)
break;
}
return -EADDRINUSE;
}
bool TCPSocket::protocol_is_disconnected() const
{
return m_state == State::Disconnecting || m_state == State::Disconnected;
}

68
Kernel/Net/TCPSocket.h Normal file
View file

@ -0,0 +1,68 @@
#pragma once
#include <Kernel/Net/IPv4Socket.h>
class TCPSocket final : public IPv4Socket {
public:
static Retained<TCPSocket> create(int protocol);
virtual ~TCPSocket() override;
enum class State {
Disconnected,
Connecting,
Connected,
Disconnecting,
};
State state() const { return m_state; }
void set_state(State state) { m_state = state; }
void set_ack_number(dword n) { m_ack_number = n; }
void set_sequence_number(dword n) { m_sequence_number = n; }
dword ack_number() const { return m_ack_number; }
dword sequence_number() const { return m_sequence_number; }
void send_tcp_packet(word flags, const void* = nullptr, int = 0);
static Lockable<HashMap<word, TCPSocket*>>& sockets_by_port();
static TCPSocketHandle from_port(word);
private:
explicit TCPSocket(int protocol);
NetworkOrdered<word> compute_tcp_checksum(const IPv4Address& source, const IPv4Address& destination, const TCPPacket&, word payload_size);
virtual int protocol_receive(const ByteBuffer&, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length) override;
virtual int protocol_send(const void*, int) override;
virtual KResult protocol_connect() override;
virtual int protocol_allocate_source_port() override;
virtual bool protocol_is_disconnected() const override;
dword m_sequence_number { 0 };
dword m_ack_number { 0 };
State m_state { State::Disconnected };
};
class TCPSocketHandle : public SocketHandle {
public:
TCPSocketHandle() { }
TCPSocketHandle(RetainPtr<TCPSocket>&& socket)
: SocketHandle(move(socket))
{
}
TCPSocketHandle(TCPSocketHandle&& other)
: SocketHandle(move(other))
{
}
TCPSocketHandle(const TCPSocketHandle&) = delete;
TCPSocketHandle& operator=(const TCPSocketHandle&) = delete;
TCPSocket* operator->() { return &socket(); }
const TCPSocket* operator->() const { return &socket(); }
TCPSocket& socket() { return static_cast<TCPSocket&>(SocketHandle::socket()); }
const TCPSocket& socket() const { return static_cast<const TCPSocket&>(SocketHandle::socket()); }
};

32
Kernel/Net/UDP.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <Kernel/Net/IPv4.h>
class [[gnu::packed]] UDPPacket {
public:
UDPPacket() { }
~UDPPacket() { }
word source_port() const { return m_source_port; }
void set_source_port(word port) { m_source_port = port; }
word destination_port() const { return m_destination_port; }
void set_destination_port(word port) { m_destination_port = port; }
word length() const { return m_length; }
void set_length(word length) { m_length = length; }
word checksum() const { return m_checksum; }
void set_checksum(word checksum) { m_checksum = checksum; }
const void* payload() const { return this + 1; }
void* payload() { return this + 1; }
private:
NetworkOrdered<word> m_source_port;
NetworkOrdered<word> m_destination_port;
NetworkOrdered<word> m_length;
NetworkOrdered<word> m_checksum;
};
static_assert(sizeof(UDPPacket) == 8);

111
Kernel/Net/UDPSocket.cpp Normal file
View file

@ -0,0 +1,111 @@
#include <Kernel/Net/UDPSocket.h>
#include <Kernel/Net/UDP.h>
#include <Kernel/Net/NetworkAdapter.h>
#include <Kernel/Process.h>
#include <Kernel/RandomDevice.h>
#include <Kernel/Net/Routing.h>
Lockable<HashMap<word, UDPSocket*>>& UDPSocket::sockets_by_port()
{
static Lockable<HashMap<word, UDPSocket*>>* s_map;
if (!s_map)
s_map = new Lockable<HashMap<word, UDPSocket*>>;
return *s_map;
}
UDPSocketHandle UDPSocket::from_port(word port)
{
RetainPtr<UDPSocket> socket;
{
LOCKER(sockets_by_port().lock());
auto it = sockets_by_port().resource().find(port);
if (it == sockets_by_port().resource().end())
return { };
socket = (*it).value;
ASSERT(socket);
}
return { move(socket) };
}
UDPSocket::UDPSocket(int protocol)
: IPv4Socket(SOCK_DGRAM, protocol)
{
}
UDPSocket::~UDPSocket()
{
LOCKER(sockets_by_port().lock());
sockets_by_port().resource().remove(source_port());
}
Retained<UDPSocket> UDPSocket::create(int protocol)
{
return adopt(*new UDPSocket(protocol));
}
int UDPSocket::protocol_receive(const ByteBuffer& packet_buffer, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length)
{
(void)flags;
(void)addr_length;
ASSERT(!packet_buffer.is_null());
auto& ipv4_packet = *(const IPv4Packet*)(packet_buffer.pointer());
auto& udp_packet = *static_cast<const UDPPacket*>(ipv4_packet.payload());
ASSERT(udp_packet.length() >= sizeof(UDPPacket)); // FIXME: This should be rejected earlier.
ASSERT(buffer_size >= (udp_packet.length() - sizeof(UDPPacket)));
if (addr) {
auto& ia = *(sockaddr_in*)addr;
ia.sin_port = htons(udp_packet.destination_port());
}
memcpy(buffer, udp_packet.payload(), udp_packet.length() - sizeof(UDPPacket));
return udp_packet.length() - sizeof(UDPPacket);
}
int UDPSocket::protocol_send(const void* data, int data_length)
{
auto* adapter = adapter_for_route_to(destination_address());
if (!adapter)
return -EHOSTUNREACH;
auto buffer = ByteBuffer::create_zeroed(sizeof(UDPPacket) + data_length);
auto& udp_packet = *(UDPPacket*)(buffer.pointer());
udp_packet.set_source_port(source_port());
udp_packet.set_destination_port(destination_port());
udp_packet.set_length(sizeof(UDPPacket) + data_length);
memcpy(udp_packet.payload(), data, data_length);
kprintf("sending as udp packet from %s:%u to %s:%u!\n",
adapter->ipv4_address().to_string().characters(),
source_port(),
destination_address().to_string().characters(),
destination_port());
adapter->send_ipv4(MACAddress(), destination_address(), IPv4Protocol::UDP, move(buffer));
return data_length;
}
KResult UDPSocket::protocol_connect()
{
return KSuccess;
}
int UDPSocket::protocol_allocate_source_port()
{
static const word first_ephemeral_port = 32768;
static const word last_ephemeral_port = 60999;
static const word ephemeral_port_range_size = last_ephemeral_port - first_ephemeral_port;
word first_scan_port = first_ephemeral_port + (word)(RandomDevice::random_percentage() * ephemeral_port_range_size);
LOCKER(sockets_by_port().lock());
for (word port = first_scan_port;;) {
auto it = sockets_by_port().resource().find(port);
if (it == sockets_by_port().resource().end()) {
set_source_port(port);
sockets_by_port().resource().set(port, this);
return port;
}
++port;
if (port > last_ephemeral_port)
port = first_ephemeral_port;
if (port == first_scan_port)
break;
}
return -EADDRINUSE;
}

47
Kernel/Net/UDPSocket.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include <Kernel/Net/IPv4Socket.h>
class UDPSocketHandle;
class UDPSocket final : public IPv4Socket {
public:
static Retained<UDPSocket> create(int protocol);
virtual ~UDPSocket() override;
static Lockable<HashMap<word, UDPSocket*>>& sockets_by_port();
static UDPSocketHandle from_port(word);
private:
explicit UDPSocket(int protocol);
virtual int protocol_receive(const ByteBuffer&, void* buffer, size_t buffer_size, int flags, sockaddr* addr, socklen_t* addr_length) override;
virtual int protocol_send(const void*, int) override;
virtual KResult protocol_connect() override;
virtual int protocol_allocate_source_port() override;
};
class UDPSocketHandle : public SocketHandle {
public:
UDPSocketHandle() { }
UDPSocketHandle(RetainPtr<UDPSocket>&& socket)
: SocketHandle(move(socket))
{
}
UDPSocketHandle(UDPSocketHandle&& other)
: SocketHandle(move(other))
{
}
UDPSocketHandle(const UDPSocketHandle&) = delete;
UDPSocketHandle& operator=(const UDPSocketHandle&) = delete;
UDPSocket* operator->() { return &socket(); }
const UDPSocket* operator->() const { return &socket(); }
UDPSocket& socket() { return static_cast<UDPSocket&>(SocketHandle::socket()); }
const UDPSocket& socket() const { return static_cast<const UDPSocket&>(SocketHandle::socket()); }
};