From 8a9208612eec8ddae4c418485d848ecfa0613699 Mon Sep 17 00:00:00 2001 From: Anton Kling Date: Mon, 30 Oct 2023 22:12:14 +0100 Subject: 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. --- kernel/network/arp.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/network/arp.h | 4 ++ kernel/network/bytes.c | 10 +++ kernel/network/bytes.h | 5 ++ kernel/network/ethernet.c | 93 ++++++++++++++++++++++++++++ kernel/network/ethernet.h | 5 ++ kernel/network/ipv4.c | 98 +++++++++++++++++++++++++++++ kernel/network/ipv4.h | 5 ++ kernel/network/udp.c | 64 +++++++++++++++++++ kernel/network/udp.h | 7 +++ 10 files changed, 444 insertions(+) create mode 100644 kernel/network/arp.c create mode 100644 kernel/network/arp.h create mode 100644 kernel/network/bytes.c create mode 100644 kernel/network/bytes.h create mode 100644 kernel/network/ethernet.c create mode 100644 kernel/network/ethernet.h create mode 100644 kernel/network/ipv4.c create mode 100644 kernel/network/ipv4.h create mode 100644 kernel/network/udp.c create mode 100644 kernel/network/udp.h (limited to 'kernel/network') 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 +#include +#include +#include +#include +#include +#include + +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 + +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 + +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 + +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 +#include +#include +#include +#include +#include +#include +#include + +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 + +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 +#include +#include +#include +#include +#include +#include +#include + +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 + +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 +#include +#include +#include +#include + +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 +#include + +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); -- cgit v1.2.3