diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-03-11 23:21:38 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-03-11 23:21:38 +0100 |
commit | 318b01e055a662fef05ef0ea4e03c1d269bbc4a8 (patch) | |
tree | e064de3c963e848bb3c50f9de44a8a9ed14bf912 | |
parent | 10dcd3a47f36f5f85fb07fd639fb2235496be155 (diff) | |
download | serenity-318b01e055a662fef05ef0ea4e03c1d269bbc4a8.zip |
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. :^)
-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 |