diff options
author | Anton Kling <anton@kling.gg> | 2023-10-27 22:17:24 +0200 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-30 21:49:48 +0100 |
commit | d50d18c9da3a125f0196bec89802dec1c5b0b800 (patch) | |
tree | d9a4e74906c5ab51708d148ad3728cc155a9893d /network | |
parent | 4f9ed7087cb58683d9423ab771ad76b31dac5514 (diff) |
Kernel/LibC/Networking: Be able to send UDP messages
Now it can send UDP messages to a specific IP address and libc has
enough to create a basic UDP ECHO server, that is kinda cool.
Diffstat (limited to 'network')
-rw-r--r-- | network/arp.c | 123 | ||||
-rw-r--r-- | network/arp.h | 1 | ||||
-rw-r--r-- | network/ethernet.c | 10 | ||||
-rw-r--r-- | network/ipv4.c | 67 | ||||
-rw-r--r-- | network/ipv4.h | 2 | ||||
-rw-r--r-- | network/udp.c | 19 | ||||
-rw-r--r-- | network/udp.h | 6 |
7 files changed, 188 insertions, 40 deletions
diff --git a/network/arp.c b/network/arp.c index f86c732..de6d898 100644 --- a/network/arp.c +++ b/network/arp.c @@ -20,9 +20,28 @@ struct ARP_DATA { // 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++) { @@ -33,7 +52,7 @@ void print_mac(const char *str, uint8_t *mac) { kprintf("\n"); } -void print_ip(const char *str, uint8_t *ip) { +void print_ip(const char *str, const uint8_t *ip) { kprintf("%s: ", str); for (int i = 0; i < 4; i++) { kprintf("%d", ip[i]); @@ -43,42 +62,92 @@ void print_ip(const char *str, uint8_t *ip) { 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 it is a request - assert(0x0001 == ntohs(data->opcode)); // Assert that request uses IP assert(0x0800 == ntohs(data->ptype)); assert(6 == data->hlen); assert(4 == data->plen); - print_mac("srchw: ", data->srchw); - print_ip("srcpr: ", data->srcpr); + // 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); + print_mac("dsthw: ", data->dsthw); + print_ip("dstpr: ", data->dstpr); - uint8_t mac[6]; - get_mac_address(mac); - print_mac("THIS DEVICE MAC: ", mac); - 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)); + 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/network/arp.h b/network/arp.h index 8efda97..c2beb94 100644 --- a/network/arp.h +++ b/network/arp.h @@ -1,3 +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/network/ethernet.c b/network/ethernet.c index 11a3876..e97ccbd 100644 --- a/network/ethernet.c +++ b/network/ethernet.c @@ -55,17 +55,7 @@ void handle_ethernet(const uint8_t *packet, uint64_t packet_length) { kprintf("OUR OWN CALCULATED crc: %x\n", crc32((const char *)eth_header, (packet_length - 4))); - kprintf("mac dst: "); - for (int i = 0; i < 6; i++) - kprintf("%x", eth_header->mac_dst[i]); - kprintf("\n"); - kprintf("mac src: "); - for (int i = 0; i < 6; i++) - kprintf("%x", eth_header->mac_src[i]); - kprintf("\n"); - uint16_t type = ntohs(eth_header->type); - kprintf("Etheretype: %x\n", type); switch (type) { case 0x0806: handle_arp(payload); diff --git a/network/ipv4.c b/network/ipv4.c index e7ef2c2..099aa0d 100644 --- a/network/ipv4.c +++ b/network/ipv4.c @@ -1,11 +1,78 @@ #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); diff --git a/network/ipv4.h b/network/ipv4.h index 294a0ef..f578202 100644 --- a/network/ipv4.h +++ b/network/ipv4.h @@ -1,3 +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/network/udp.c b/network/udp.c index 29f17e1..23411da 100644 --- a/network/udp.c +++ b/network/udp.c @@ -1,8 +1,24 @@ #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); @@ -10,11 +26,10 @@ void handle_udp(uint8_t src_ip[4], const uint8_t *payload, // 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); - kprintf("source_port: %d\n", h_source_port); - kprintf("dst_port: %d\n", h_dst_port); uint16_t data_length = h_length - 8; const uint8_t *data = payload + 8; diff --git a/network/udp.h b/network/udp.h index 8278b87..c30b8c8 100644 --- a/network/udp.h +++ b/network/udp.h @@ -1,3 +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 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); |