summaryrefslogtreecommitdiff
path: root/kernel/network
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-30 22:12:14 +0100
committerAnton Kling <anton@kling.gg>2023-10-31 00:18:38 +0100
commit8a9208612eec8ddae4c418485d848ecfa0613699 (patch)
tree2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/network
parentca76600acc8bf7a02346efa5bd8f17072210ec01 (diff)
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel/network')
-rw-r--r--kernel/network/arp.c153
-rw-r--r--kernel/network/arp.h4
-rw-r--r--kernel/network/bytes.c10
-rw-r--r--kernel/network/bytes.h5
-rw-r--r--kernel/network/ethernet.c93
-rw-r--r--kernel/network/ethernet.h5
-rw-r--r--kernel/network/ipv4.c98
-rw-r--r--kernel/network/ipv4.h5
-rw-r--r--kernel/network/udp.c64
-rw-r--r--kernel/network/udp.h7
10 files changed, 444 insertions, 0 deletions
diff --git a/kernel/network/arp.c b/kernel/network/arp.c
new file mode 100644
index 0000000..de6d898
--- /dev/null
+++ b/kernel/network/arp.c
@@ -0,0 +1,153 @@
+#include <assert.h>
+#include <drivers/rtl8139.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <stdio.h>
+#include <string.h>
+
+struct ARP_DATA {
+ uint16_t htype; // Hardware type
+ uint16_t ptype; // Protocol type
+ uint8_t hlen; // Hardware address length (Ethernet = 6)
+ uint8_t plen; // Protocol address length (IPv4 = 4)
+ uint16_t opcode; // ARP Operation Code
+ uint8_t srchw[6]; // Source hardware address - hlen bytes (see above)
+ uint8_t srcpr[4]; // Source protocol address - plen bytes (see above).
+ // If IPv4 can just be a "u32" type.
+ uint8_t dsthw[6]; // Destination hardware address - hlen bytes (see above)
+ uint8_t dstpr[4]; // Destination protocol address - plen bytes (see
+ // above). If IPv4 can just be a "u32" type.
+};
+
+struct ARP_TABLE_ENTRY {
+ uint8_t is_used;
+ uint8_t mac[6];
+ uint8_t ip[4];
+};
+
+struct ARP_TABLE_ENTRY arp_table[10] = {0};
+
+// FIXME: This is hardcoded, don't do this.
+uint8_t ip_address[4] = {10, 0, 2, 15};
+
+struct ARP_TABLE_ENTRY *find_arp_entry_to_use(void) {
+ // This does not need to find a "free" entry as a ARP table is
+ // just a cache, it just has to pick a entry efficently.
+ for (int i = 0; i < 10; i++) {
+ if (!arp_table[i].is_used) {
+ return &arp_table[i];
+ }
+ }
+ return &arp_table[0];
+}
+
+void print_mac(const char *str, uint8_t *mac) {
+ kprintf("%s: ", str);
+ for (int i = 0; i < 6; i++) {
+ kprintf("%x", mac[i]);
+ if (5 != i)
+ kprintf(":");
+ }
+ kprintf("\n");
+}
+
+void print_ip(const char *str, const uint8_t *ip) {
+ kprintf("%s: ", str);
+ for (int i = 0; i < 4; i++) {
+ kprintf("%d", ip[i]);
+ if (3 != i)
+ kprintf(".");
+ }
+ kprintf("\n");
+}
+
+void send_arp_request(const uint8_t ip[4]) {
+ struct ARP_DATA data;
+ data.htype = htons(1);
+ data.ptype = htons(0x0800);
+
+ data.hlen = 6;
+ data.plen = 4;
+
+ data.opcode = htons(0x0001);
+ get_mac_address(data.srchw);
+ memcpy(data.srcpr, ip_address, sizeof(uint8_t[4]));
+
+ memset(data.dsthw, 0, sizeof(uint8_t[6]));
+ memcpy(data.dstpr, ip, sizeof(uint8_t[4]));
+
+ uint8_t broadcast[6];
+ memset(broadcast, 0xFF, sizeof(broadcast));
+ send_ethernet_packet(broadcast, 0x0806, (uint8_t *)&data, sizeof(data));
+}
+
+int get_mac_from_ip(const uint8_t ip[4], uint8_t mac[6]) {
+ print_ip("ARP GETTING MAC FROM IP: ", ip);
+ for (int i = 0; i < 10; i++) {
+ if (0 != memcmp(arp_table[i].ip, ip, sizeof(uint8_t[4])))
+ continue;
+ memcpy(mac, arp_table[i].mac, sizeof(uint8_t[6]));
+ return 1;
+ }
+ klog("ARP cache miss", LOG_NOTE);
+ asm("sti");
+ send_arp_request(ip);
+ // TODO: Maybe wait a bit?
+ for (int i = 0; i < 10; i++) {
+ if (0 != memcmp(arp_table[i].ip, ip, sizeof(uint8_t[4])))
+ continue;
+ memcpy(mac, arp_table[i].mac, sizeof(uint8_t[6]));
+ return 1;
+ }
+ assert(0);
+ return 0;
+}
+
+void handle_arp(const uint8_t *payload) {
+ struct ARP_DATA *data = (struct ARP_DATA *)payload;
+
+ // Assert that communication is over ethernet
+ assert(1 == ntohs(data->htype));
+ // Assert that request uses IP
+ assert(0x0800 == ntohs(data->ptype));
+
+ assert(6 == data->hlen);
+ assert(4 == data->plen);
+ // Assert it is a request
+ if (0x0001 /*arp_request*/ == ntohs(data->opcode)) {
+ print_mac("srchw: ", data->srchw);
+ print_ip("srcpr: ", data->srcpr);
+
+ print_mac("dsthw: ", data->dsthw);
+ print_ip("dstpr: ", data->dstpr);
+
+ assert(0 == memcmp(data->dstpr, ip_address, sizeof(uint8_t[4])));
+
+ // Now we have to construct a ARP response
+ struct ARP_DATA response;
+ response.htype = htons(1);
+ response.ptype = htons(0x0800);
+ response.opcode = htons(0x00002);
+ response.hlen = 6;
+ response.plen = 4;
+ get_mac_address(response.srchw);
+ memcpy(response.srcpr, ip_address, sizeof(uint8_t[4]));
+
+ memcpy(response.dsthw, data->srchw, sizeof(uint8_t[6]));
+ memcpy(response.dstpr, data->srcpr, sizeof(uint8_t[4]));
+
+ send_ethernet_packet(data->srchw, 0x0806, (uint8_t *)&response,
+ sizeof(response));
+ } else if (0x0002 /*arp_response*/ == ntohs(data->opcode)) {
+ // Find a entry to fill
+ struct ARP_TABLE_ENTRY *entry = find_arp_entry_to_use();
+ entry->is_used = 1;
+ memcpy(entry->mac, data->srchw, sizeof(uint8_t[6]));
+ memcpy(entry->ip, data->srcpr, sizeof(uint8_t[4]));
+ print_ip("Added ip: ", entry->ip);
+ } else {
+ kprintf("GOT A ARP REQEUST WITH TYPE: %x\n", ntohs(data->opcode));
+ assert(0);
+ }
+}
diff --git a/kernel/network/arp.h b/kernel/network/arp.h
new file mode 100644
index 0000000..c2beb94
--- /dev/null
+++ b/kernel/network/arp.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+
+int get_mac_from_ip(const uint8_t ip[4], uint8_t mac[6]);
+void handle_arp(const uint8_t *payload);
diff --git a/kernel/network/bytes.c b/kernel/network/bytes.c
new file mode 100644
index 0000000..94afa73
--- /dev/null
+++ b/kernel/network/bytes.c
@@ -0,0 +1,10 @@
+#include <network/bytes.h>
+
+uint16_t ntohs(uint16_t net) { return (net >> 8) | (net << 8); }
+
+uint16_t htons(uint16_t net) { return (net >> 8) | (net << 8); }
+
+uint32_t htonl(uint32_t net) {
+ return (((net & 0x000000FF) << 24) | ((net & 0x0000FF00) << 8) |
+ ((net & 0x00FF0000) >> 8) | ((net & 0xFF000000) >> 24));
+}
diff --git a/kernel/network/bytes.h b/kernel/network/bytes.h
new file mode 100644
index 0000000..c291589
--- /dev/null
+++ b/kernel/network/bytes.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+uint16_t ntohs(uint16_t net);
+uint16_t htons(uint16_t net);
+uint32_t htonl(uint32_t net);
diff --git a/kernel/network/ethernet.c b/kernel/network/ethernet.c
new file mode 100644
index 0000000..e97ccbd
--- /dev/null
+++ b/kernel/network/ethernet.c
@@ -0,0 +1,93 @@
+#include <assert.h>
+#include <drivers/rtl8139.h>
+#include <kmalloc.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <network/ipv4.h>
+#include <stdio.h>
+
+struct ETHERNET_HEADER {
+ uint8_t mac_dst[6];
+ uint8_t mac_src[6];
+ uint16_t type;
+};
+
+uint32_t crc32(const char *buf, size_t len) {
+ static uint32_t table[256];
+ static int have_table = 0;
+ uint32_t rem;
+ uint8_t octet;
+ int i, j;
+ const char *p, *q;
+
+ if (have_table == 0) {
+ for (i = 0; i < 256; i++) {
+ rem = i;
+ for (j = 0; j < 8; j++) {
+ if (rem & 1) {
+ rem >>= 1;
+ rem ^= 0xedb88320;
+ } else
+ rem >>= 1;
+ }
+ table[i] = rem;
+ }
+ have_table = 1;
+ }
+
+ uint32_t crc = 0xFFFFFFFF;
+ q = buf + len;
+ for (p = buf; p < q; p++) {
+ octet = *p;
+ crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet];
+ }
+ return ~crc;
+}
+
+void handle_ethernet(const uint8_t *packet, uint64_t packet_length) {
+ struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)packet;
+ packet += sizeof(struct ETHERNET_HEADER);
+ const uint8_t *payload = packet;
+ packet += packet_length - sizeof(struct ETHERNET_HEADER);
+ uint32_t crc = *((uint32_t *)packet - 1);
+ kprintf("PACKET crc: %x\n", crc);
+ kprintf("OUR OWN CALCULATED crc: %x\n",
+ crc32((const char *)eth_header, (packet_length - 4)));
+
+ uint16_t type = ntohs(eth_header->type);
+ switch (type) {
+ case 0x0806:
+ handle_arp(payload);
+ break;
+ case 0x0800:
+ handle_ipv4(payload, packet_length - sizeof(struct ETHERNET_HEADER) - 4);
+ break;
+ default:
+ kprintf("Can't handle ethernet type\n");
+ break;
+ }
+}
+
+void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload,
+ uint64_t payload_length) {
+ // FIXME: Janky allocation, do this better
+ uint64_t buffer_size =
+ sizeof(struct ETHERNET_HEADER) + payload_length + sizeof(uint32_t);
+ uint8_t *buffer = kmalloc(buffer_size);
+ uint8_t *buffer_start = buffer;
+ struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)buffer;
+ buffer += sizeof(struct ETHERNET_HEADER);
+ memcpy(buffer, payload, payload_length);
+ buffer += payload_length;
+
+ memcpy(eth_header->mac_dst, mac_dst, sizeof(uint8_t[6]));
+ get_mac_address(eth_header->mac_src);
+ eth_header->type = htons(type);
+ *(uint32_t *)(buffer) =
+ htonl(crc32((const char *)buffer_start, buffer_size - 4));
+
+ assert(rtl8139_send_data(buffer_start, buffer_size));
+ kfree(buffer_start);
+ kprintf("sent data\n");
+}
diff --git a/kernel/network/ethernet.h b/kernel/network/ethernet.h
new file mode 100644
index 0000000..0fdcee3
--- /dev/null
+++ b/kernel/network/ethernet.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+void handle_ethernet(const uint8_t *packet, uint64_t packet_length);
+void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload,
+ uint64_t payload_length);
diff --git a/kernel/network/ipv4.c b/kernel/network/ipv4.c
new file mode 100644
index 0000000..099aa0d
--- /dev/null
+++ b/kernel/network/ipv4.c
@@ -0,0 +1,98 @@
+#include <assert.h>
+#include <kmalloc.h>
+#include <network/arp.h>
+#include <network/bytes.h>
+#include <network/ethernet.h>
+#include <network/ipv4.h>
+#include <network/udp.h>
+#include <string.h>
+
+uint16_t ip_checksum(void *vdata, size_t length) {
+ // Cast the data pointer to one that can be indexed.
+ char *data = (char *)vdata;
+
+ // Initialise the accumulator.
+ uint32_t acc = 0xffff;
+
+ // Handle complete 16-bit blocks.
+ for (size_t i = 0; i + 1 < length; i += 2) {
+ uint16_t word;
+ memcpy(&word, data + i, 2);
+ acc += ntohs(word);
+ if (acc > 0xffff) {
+ acc -= 0xffff;
+ }
+ }
+
+ // Handle any partial block at the end of the data.
+ if (length & 1) {
+ uint16_t word = 0;
+ memcpy(&word, data + length - 1, 1);
+ acc += ntohs(word);
+ if (acc > 0xffff) {
+ acc -= 0xffff;
+ }
+ }
+
+ // Return the checksum in network byte order.
+ return htons(~acc);
+}
+
+extern uint8_t ip_address[4];
+void send_ipv4_packet(uint32_t ip, uint8_t protocol, const uint8_t *payload,
+ uint16_t length) {
+ uint8_t header[20] = {0};
+ header[0] = (4 /*version*/ << 4) | (5 /*IHL*/);
+ *((uint16_t *)(header + 2)) = htons(length + 20);
+ header[8 /*TTL*/] = 0xF8;
+ header[9] = protocol;
+
+ memcpy(header + 12 /*src_ip*/, ip_address, sizeof(uint8_t[4]));
+ memcpy(header + 16, &ip, sizeof(uint8_t[4]));
+
+ *((uint16_t *)(header + 10 /*checksum*/)) = ip_checksum(header, 20);
+ uint16_t packet_length = length + 20;
+ uint8_t *packet = kmalloc(packet_length);
+ memcpy(packet, header, 20);
+ memcpy(packet + 20, payload, length);
+
+ uint8_t mac[6];
+ uint8_t ip_copy[4]; // TODO: Do I need to do this?
+ memcpy(ip_copy, &ip, sizeof(uint8_t[4]));
+ get_mac_from_ip(ip_copy, mac);
+ send_ethernet_packet(mac, 0x0800, packet, packet_length);
+ kfree(packet);
+}
+
+void handle_ipv4(const uint8_t *payload, uint32_t packet_length) {
+ assert(packet_length > 4);
+
+ uint16_t saved_checksum = *(uint16_t *)(payload + 10);
+ *(uint16_t *)(payload + 10) = 0;
+ uint16_t calc_checksum = ip_checksum((uint8_t *)payload, 20);
+ *(uint16_t *)(payload + 10) = saved_checksum;
+ assert(calc_checksum == saved_checksum);
+
+ uint8_t version = (*payload & 0xF0) >> 4;
+ uint8_t IHL = (*payload & 0xF);
+ kprintf("version: %x\n", version);
+ assert(4 == version);
+ assert(5 == IHL);
+ uint16_t ipv4_total_length = ntohs(*(uint16_t *)(payload + 2));
+ assert(ipv4_total_length >= 20);
+ // Make sure the ipv4 header is not trying to get uninitalized memory
+ assert(ipv4_total_length <= packet_length);
+
+ uint8_t src_ip[4];
+ memcpy(src_ip, payload + 12, sizeof(uint8_t[4]));
+
+ uint8_t protocol = *(payload + 9);
+ switch (protocol) {
+ case 0x11:
+ handle_udp(src_ip, payload + 20, ipv4_total_length - 20);
+ break;
+ default:
+ kprintf("Protocol given in IPv4 header not handeld: %x\n", protocol);
+ break;
+ }
+}
diff --git a/kernel/network/ipv4.h b/kernel/network/ipv4.h
new file mode 100644
index 0000000..f578202
--- /dev/null
+++ b/kernel/network/ipv4.h
@@ -0,0 +1,5 @@
+#include <stdint.h>
+
+void handle_ipv4(const uint8_t *payload, uint32_t packet_length);
+void send_ipv4_packet(uint32_t ip, uint8_t protocol, const uint8_t *payload,
+ uint16_t length);
diff --git a/kernel/network/udp.c b/kernel/network/udp.c
new file mode 100644
index 0000000..23411da
--- /dev/null
+++ b/kernel/network/udp.c
@@ -0,0 +1,64 @@
+#include <assert.h>
+#include <network/bytes.h>
+#include <network/ipv4.h>
+#include <network/udp.h>
+#include <socket.h>
+
+void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
+ const uint8_t *payload, uint16_t payload_length) {
+ uint16_t header[4] = {0};
+ header[0] = src->sin_port;
+ header[1] = dst->sin_port;
+ header[2] = htons(payload_length + 8);
+
+ uint16_t packet_length = sizeof(header) + payload_length;
+ uint8_t *packet = kmalloc(packet_length);
+ memcpy(packet, header, sizeof(header));
+ memcpy(packet + sizeof(header), payload, payload_length);
+ send_ipv4_packet(dst->sin_addr.s_addr, 0x11, packet, packet_length);
+ kfree(packet);
+}
+
+void handle_udp(uint8_t src_ip[4], const uint8_t *payload,
+ uint32_t packet_length) {
+ assert(packet_length >= 8);
+ // n_.* means network format(big endian)
+ // h_.* means host format((probably) little endian)
+ uint16_t n_source_port = *(uint16_t *)payload;
+ uint16_t h_source_port = ntohs(n_source_port);
+ (void)h_source_port;
+ uint16_t h_dst_port = ntohs(*(uint16_t *)(payload + 2));
+ uint16_t h_length = ntohs(*(uint16_t *)(payload + 4));
+ assert(h_length == packet_length);
+ uint16_t data_length = h_length - 8;
+ const uint8_t *data = payload + 8;
+
+ // Find the open port
+ OPEN_INET_SOCKET *in_s = find_open_udp_port(htons(h_dst_port));
+ assert(in_s);
+ SOCKET *s = in_s->s;
+ vfs_fd_t *fifo_file = s->ptr_socket_fd;
+
+ // Write the sockaddr struct such that it can later be
+ // given to userland if asked.
+ struct sockaddr_in /*{
+ sa_family_t sin_family;
+ union {
+ uint32_t s_addr;
+ } sin_addr;
+ uint16_t sin_port;
+ }*/ in;
+ in.sin_family = AF_INET;
+ memcpy(&in.sin_addr.s_addr, src_ip, sizeof(uint32_t));
+ in.sin_port = n_source_port;
+ socklen_t sock_length = sizeof(struct sockaddr_in);
+
+ raw_vfs_pwrite(fifo_file, &sock_length, sizeof(sock_length), 0);
+ raw_vfs_pwrite(fifo_file, &in, sizeof(in), 0);
+
+ // Write the UDP payload length(not including header)
+ raw_vfs_pwrite(fifo_file, &data_length, sizeof(uint16_t), 0);
+
+ // Write the UDP payload
+ raw_vfs_pwrite(fifo_file, (char *)data, data_length, 0);
+}
diff --git a/kernel/network/udp.h b/kernel/network/udp.h
new file mode 100644
index 0000000..c30b8c8
--- /dev/null
+++ b/kernel/network/udp.h
@@ -0,0 +1,7 @@
+#include <socket.h>
+#include <stdint.h>
+
+void handle_udp(uint8_t src_ip[4], const uint8_t *payload,
+ uint32_t packet_length);
+void send_udp_packet(struct sockaddr_in *src, const struct sockaddr_in *dst,
+ const uint8_t *payload, uint16_t payload_length);