Kernel: Bring up enough networking code that we can respond to ARP requests.

This is all pretty rickety but we can now respond to "arping" from the host
while running inside QEMU. Very cool. :^)
This commit is contained in:
Andreas Kling 2019-03-11 23:21:38 +01:00
parent 10dcd3a47f
commit 318b01e055
12 changed files with 229 additions and 40 deletions

1
Kernel/.gitignore vendored
View file

@ -6,3 +6,4 @@ kernel
kernel.map
_fs_contents
sync-local.sh
*.pcap

View file

@ -1,14 +1,48 @@
#pragma once
#include <Kernel/MACAddress.h>
#include <Kernel/IPv4Address.h>
#include <Kernel/EtherType.h>
class [[gnu::packed]] ARPPacket {
public:
word hardware_type;
word protocol_type;
word hardware_address_length;
word protocol_address_length;
word operation;
uint8_t sender_hardware_address[6]; // Sender hardware address.
uint8_t sender_protocol_address[4]; // Sender protocol address.
uint8_t target_hardware_address[6]; // target hardware address.
uint8_t target_protocol_address[4]; // target protocol address.
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

@ -230,7 +230,6 @@ void E1000NetworkAdapter::initialize_rx_descriptors()
out32(REG_RXDESCLEN, number_of_rx_descriptors * sizeof(e1000_rx_desc));
out32(REG_RXDESCHEAD, 0);
out32(REG_RXDESCTAIL, number_of_rx_descriptors - 1);
m_rx_current = 0;
out32(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_8192);
}
@ -244,7 +243,7 @@ void E1000NetworkAdapter::initialize_tx_descriptors()
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 = 0;
descriptor.addr = (qword)kmalloc_eternal(8192 + 16);
descriptor.cmd = 0;
}
@ -252,10 +251,9 @@ void E1000NetworkAdapter::initialize_tx_descriptors()
out32(REG_TXDESCHI, 0);
out32(REG_TXDESCLEN, number_of_tx_descriptors * sizeof(e1000_tx_desc));
out32(REG_TXDESCHEAD, 0);
out32(REG_TXDESCTAIL, number_of_tx_descriptors - 1);
m_tx_current = 0;
out32(REG_TXDESCTAIL, 0);
out32(REG_TCTRL, 0b0110000000000111111000011111010);
out32(REG_TCTRL, in32(REG_TCTRL) | TCTL_EN | TCTL_PSP);
out32(REG_TIPG, 0x0060200A);
}
@ -312,29 +310,45 @@ dword E1000NetworkAdapter::in32(word 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);
auto& descriptor = m_tx_descriptors[m_tx_current];
descriptor.addr = (uint64_t)data;
#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;
m_tx_current = (m_tx_current + 1) % number_of_tx_descriptors;
out32(REG_TXDESCTAIL, m_tx_current);
while (!(descriptor.status & 0xff))
#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)
;
kprintf("E1000: Sent packet!\n");
#ifdef E1000_DEBUG
kprintf("E1000: Sent packet, status is now %b!\n", descriptor.status);
#endif
}
void E1000NetworkAdapter::receive()
{
while (m_rx_descriptors[m_rx_current].status & 1) {
auto* buffer = (byte*)m_rx_descriptors[m_rx_current].addr;
word length = m_rx_descriptors[m_rx_current].length;
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[m_rx_current].status = 0;
auto old_current = m_rx_current;
m_rx_current = (m_rx_current + 1) % number_of_rx_descriptors;
out32(REG_RXDESCTAIL, old_current);
m_rx_descriptors[rx_current].status = 0;
out32(REG_RXDESCTAIL, rx_current);
}
}

View file

@ -71,6 +71,4 @@ private:
e1000_rx_desc* m_rx_descriptors;
e1000_tx_desc* m_tx_descriptors;
word m_rx_current { 0 };
word m_tx_current { 0 };
};

10
Kernel/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

@ -13,8 +13,8 @@ public:
MACAddress source() const { return m_source; }
void set_source(const MACAddress& address) { m_source = address; }
word ether_type() const { return (m_ether_type & 0xff) << 16 | ((m_ether_type >> 16) & 0xff); }
void set_ether_type(word ether_type) { m_ether_type = (ether_type & 0xff) << 16 | ((ether_type >> 16) & 0xff); }
word ether_type() const { return ntohs(m_ether_type); }
void set_ether_type(word ether_type) { m_ether_type = htons(ether_type); }
const void* payload() const { return &m_payload[0]; }
void* payload() { return &m_payload[0]; }

44
Kernel/IPv4Address.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include <AK/Assertions.h>
#include <AK/AKString.h>
#include <AK/Types.h>
#include <Kernel/StdLib.h>
class [[gnu::packed]] IPv4Address {
public:
IPv4Address() { }
IPv4Address(const byte data[4])
{
memcpy(m_data, data, 4);
}
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;
}
~IPv4Address() { }
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; }
private:
union {
byte m_data[4];
dword m_data_as_dword;
};
};
static_assert(sizeof(IPv4Address) == 4);

View file

@ -2,6 +2,7 @@
#include <Kernel/StdLib.h>
#include <Kernel/EthernetFrameHeader.h>
#include <Kernel/kmalloc.h>
#include <Kernel/EtherType.h>
NetworkAdapter::NetworkAdapter()
{
@ -17,6 +18,7 @@ void NetworkAdapter::send(const MACAddress& destination, const ARPPacket& packet
auto* eth = (EthernetFrameHeader*)kmalloc(size_in_bytes);
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);
kfree(eth);
@ -35,3 +37,8 @@ ByteBuffer NetworkAdapter::dequeue_packet()
return { };
return m_packet_queue.take_first();
}
void NetworkAdapter::set_ipv4_address(const IPv4Address& address)
{
m_ipv4_address = address;
}

View file

@ -4,6 +4,7 @@
#include <AK/SinglyLinkedList.h>
#include <AK/Types.h>
#include <Kernel/MACAddress.h>
#include <Kernel/IPv4Address.h>
#include <Kernel/ARPPacket.h>
class NetworkAdapter {
@ -12,6 +13,9 @@ public:
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&);
@ -25,5 +29,6 @@ protected:
private:
MACAddress m_mac_address;
IPv4Address m_ipv4_address;
SinglyLinkedList<ByteBuffer> m_packet_queue;
};

View file

@ -2,18 +2,24 @@
#include <Kernel/EthernetFrameHeader.h>
#include <Kernel/ARPPacket.h>
#include <Kernel/Process.h>
#include <Kernel/EtherType.h>
static void handle_arp(const EthernetFrameHeader&, int frame_size);
static void handle_ipv4(const EthernetFrameHeader&, int frame_size);
void NetworkTask_main()
{
auto* e1000_ptr = E1000NetworkAdapter::the();
ASSERT(e1000_ptr);
auto& e1000 = *e1000_ptr;
e1000.set_ipv4_address(IPv4Address(192, 168, 5, 2));
ARPPacket arp;
arp.hardware_type = 1; // Ethernet
arp.hardware_address_length = 6; // MAC length
arp.protocol_type = 0x0800; // IPv4
arp.protocol_address_length = 4; // IP length
arp.operation = 1; // 1 (request)
arp.set_hardware_type(1); // Ethernet
arp.set_hardware_address_length(sizeof(MACAddress));
arp.set_protocol_type(EtherType::IPv4);
arp.set_protocol_address_length(sizeof(IPv4Address));
arp.set_operation(1); // Request
e1000.send(MACAddress(), arp);
kprintf("NetworkTask: Enter main loop.\n");
@ -27,7 +33,74 @@ void NetworkTask_main()
kprintf("NetworkTask: Packet is too small to be an Ethernet packet! (%d)\n", packet.size());
continue;
}
auto* eth = (const EthernetFrameHeader*)packet.pointer();
kprintf("NetworkTask: Handle packet from %s to %s\n", eth->source().to_string().characters(), eth->destination().to_string().characters());
auto& eth = *(const EthernetFrameHeader*)packet.pointer();
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()
);
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) + 4;
if (frame_size < minimum_arp_frame_size) {
kprintf("handle_arp: Frame too small (%d, need %d)\n", frame_size, minimum_arp_frame_size);
return;
}
const ARPPacket& incoming_packet = *static_cast<const ARPPacket*>(eth.payload());
if (incoming_packet.hardware_type() != 1 || incoming_packet.hardware_address_length() != sizeof(MACAddress)) {
kprintf("handle_arp: Hardware type not ethernet (%w, len=%u)\n", incoming_packet.hardware_type(), incoming_packet.hardware_address_length());
return;
}
if (incoming_packet.protocol_type() != EtherType::IPv4 || incoming_packet.protocol_address_length() != sizeof(IPv4Address)) {
kprintf("handle_arp: Protocol type not IPv4 (%w, len=%u)\n", incoming_packet.hardware_type(), incoming_packet.protocol_address_length());
return;
}
#ifdef ARP_DEBUG
kprintf("handle_arp: operation=%w, sender=%s/%s, target=%s/%s\n",
incoming_packet.operation(),
incoming_packet.sender_hardware_address().to_string().characters(),
incoming_packet.sender_protocol_address().to_string().characters(),
incoming_packet.target_hardware_address().to_string().characters(),
incoming_packet.target_protocol_address().to_string().characters()
);
#endif
// FIXME: Get the adapter through some kind of lookup by IPv4 address.
auto& e1000 = *E1000NetworkAdapter::the();
if (incoming_packet.operation() == 1) {
// Who has this IP address?
if (e1000.ipv4_address() == incoming_packet.target_protocol_address()) {
// We do!
kprintf("handle_arp: Responding to ARP request for my IPv4 address (%s)\n", e1000.ipv4_address().to_string().characters());
ARPPacket response;
response.set_operation(2); // Response
response.set_target_hardware_address(incoming_packet.sender_hardware_address());
response.set_target_protocol_address(incoming_packet.sender_protocol_address());
response.set_sender_hardware_address(e1000.mac_address());
response.set_sender_protocol_address(e1000.ipv4_address());
e1000.send(incoming_packet.sender_hardware_address(), response);
}
}
}
void handle_ipv4(const EthernetFrameHeader& eth, int frame_size)
{
}

View file

@ -16,4 +16,7 @@ char *strdup(const char*);
int memcmp(const void*, const void*, size_t);
char* strrchr(const char* str, int ch);
inline word ntohs(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); }
inline word htons(word w) { return (w & 0xff) << 8 | ((w >> 8) & 0xff); }
}

View file

@ -9,6 +9,6 @@ elif [ "$1" = "qn" ]; then
else
echo run with net
# ./run: qemu with network
sudo qemu-system-i386 -s -m 32 -netdev tap,id=br0 -device e1000,netdev=br0 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents #$
sudo qemu-system-i386 -s -m 32 -object filter-dump,id=hue,netdev=br0,file=e1000.pcap -netdev tap,ifname=tap0,id=br0 -device e1000,netdev=br0 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents
fi