diff options
-rw-r--r-- | Kernel/.gitignore | 1 | ||||
-rw-r--r-- | Kernel/ARPPacket.h | 52 | ||||
-rw-r--r-- | Kernel/E1000NetworkAdapter.cpp | 52 | ||||
-rw-r--r-- | Kernel/E1000NetworkAdapter.h | 2 | ||||
-rw-r--r-- | Kernel/EtherType.h | 10 | ||||
-rw-r--r-- | Kernel/EthernetFrameHeader.h | 4 | ||||
-rw-r--r-- | Kernel/IPv4Address.h | 44 | ||||
-rw-r--r-- | Kernel/NetworkAdapter.cpp | 7 | ||||
-rw-r--r-- | Kernel/NetworkAdapter.h | 5 | ||||
-rw-r--r-- | Kernel/NetworkTask.cpp | 87 | ||||
-rw-r--r-- | Kernel/StdLib.h | 3 | ||||
-rwxr-xr-x | Kernel/run | 2 |
12 files changed, 229 insertions, 40 deletions
diff --git a/Kernel/.gitignore b/Kernel/.gitignore index 69c0ff7371..cef27ed586 100644 --- a/Kernel/.gitignore +++ b/Kernel/.gitignore @@ -6,3 +6,4 @@ kernel kernel.map _fs_contents sync-local.sh +*.pcap diff --git a/Kernel/ARPPacket.h b/Kernel/ARPPacket.h index f77f10b443..72cb75b625 100644 --- a/Kernel/ARPPacket.h +++ b/Kernel/ARPPacket.h @@ -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); diff --git a/Kernel/E1000NetworkAdapter.cpp b/Kernel/E1000NetworkAdapter.cpp index b70bf79a0d..05e1c79253 100644 --- a/Kernel/E1000NetworkAdapter.cpp +++ b/Kernel/E1000NetworkAdapter.cpp @@ -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); } } diff --git a/Kernel/E1000NetworkAdapter.h b/Kernel/E1000NetworkAdapter.h index 55390561ce..0bd47cda8b 100644 --- a/Kernel/E1000NetworkAdapter.h +++ b/Kernel/E1000NetworkAdapter.h @@ -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 }; }; diff --git a/Kernel/EtherType.h b/Kernel/EtherType.h new file mode 100644 index 0000000000..027967ff77 --- /dev/null +++ b/Kernel/EtherType.h @@ -0,0 +1,10 @@ +#pragma once + +#include <AK/Types.h> + +struct EtherType { +enum : word { + ARP = 0x0806, + IPv4 = 0x0800, +}; +}; diff --git a/Kernel/EthernetFrameHeader.h b/Kernel/EthernetFrameHeader.h index a90ca5503b..f6f74b42bb 100644 --- a/Kernel/EthernetFrameHeader.h +++ b/Kernel/EthernetFrameHeader.h @@ -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]; } diff --git a/Kernel/IPv4Address.h b/Kernel/IPv4Address.h new file mode 100644 index 0000000000..d1f82c0114 --- /dev/null +++ b/Kernel/IPv4Address.h @@ -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); diff --git a/Kernel/NetworkAdapter.cpp b/Kernel/NetworkAdapter.cpp index 6d46c5c660..b7e29967ae 100644 --- a/Kernel/NetworkAdapter.cpp +++ b/Kernel/NetworkAdapter.cpp @@ -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; +} diff --git a/Kernel/NetworkAdapter.h b/Kernel/NetworkAdapter.h index 38ccb8ce10..01c69b9a25 100644 --- a/Kernel/NetworkAdapter.h +++ b/Kernel/NetworkAdapter.h @@ -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; }; diff --git a/Kernel/NetworkTask.cpp b/Kernel/NetworkTask.cpp index 763331cc5f..1da5238a59 100644 --- a/Kernel/NetworkTask.cpp +++ b/Kernel/NetworkTask.cpp @@ -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) +{ + +} diff --git a/Kernel/StdLib.h b/Kernel/StdLib.h index 05525fb4a6..3af0ba98d7 100644 --- a/Kernel/StdLib.h +++ b/Kernel/StdLib.h @@ -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); } + } diff --git a/Kernel/run b/Kernel/run index d4a8ed41b6..152539c1eb 100755 --- a/Kernel/run +++ b/Kernel/run @@ -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 |