/** * @file kernel/net/arp.c * @brief Address resolution * * @copyright * This file is part of ToaruOS and is released under the terms * of the NCSA / University of Illinois License - see LICENSE.md * Copyright (C) 2021 K. Lange */ #include #include #include #include #include #include #include #include #include #include #ifndef MISAKA_DEBUG_NET #define printf(...) #endif struct arp_header { uint16_t arp_htype; uint16_t arp_ptype; uint8_t arp_hlen; uint8_t arp_plen; uint16_t arp_oper; union { struct { uint8_t arp_sha[6]; uint32_t arp_spa; uint8_t arp_tha[6]; uint32_t arp_tpa; } __attribute__((packed)) arp_eth_ipv4; } arp_data; } __attribute__((packed)); static void ip_ntoa(const uint32_t src_addr, char * out) { snprintf(out, 16, "%d.%d.%d.%d", (src_addr & 0xFF000000) >> 24, (src_addr & 0xFF0000) >> 16, (src_addr & 0xFF00) >> 8, (src_addr & 0xFF)); } spin_lock_t net_arp_cache_lock = {0}; hashmap_t * net_arp_cache = NULL; void net_arp_cache_add(struct EthernetDevice * iface, uint32_t addr, uint8_t * hwaddr, uint16_t flags) { spin_lock(net_arp_cache_lock); struct ArpCacheEntry * entry = hashmap_get(net_arp_cache, (void*)(uintptr_t)addr); if (!entry) entry = malloc(sizeof(struct ArpCacheEntry)); memcpy(entry->hwaddr, hwaddr, 6); entry->flags = flags; entry->iface = iface; hashmap_set(net_arp_cache, (void*)(uintptr_t)addr, entry); spin_unlock(net_arp_cache_lock); } struct ArpCacheEntry * net_arp_cache_get(uint32_t addr) { spin_lock(net_arp_cache_lock); struct ArpCacheEntry * out = hashmap_get(net_arp_cache, (void*)(uintptr_t)addr); spin_unlock(net_arp_cache_lock); return out; } void net_arp_ask(uint32_t addr, fs_node_t * fsnic) { struct EthernetDevice * ethnic = fsnic->device; struct arp_header arp_request = {0}; arp_request.arp_htype = htons(1); /* Ethernet */ arp_request.arp_ptype = htons(ETHERNET_TYPE_IPV4); arp_request.arp_hlen = 6; arp_request.arp_plen = 4; arp_request.arp_oper = htons(1); /* Who is...? */ arp_request.arp_data.arp_eth_ipv4.arp_tpa = addr; memcpy(arp_request.arp_data.arp_eth_ipv4.arp_sha, ethnic->mac, 6); if (ethnic->ipv4_addr) { arp_request.arp_data.arp_eth_ipv4.arp_spa = ethnic->ipv4_addr; } net_eth_send(ethnic, sizeof(struct arp_header), &arp_request, ETHERNET_TYPE_ARP, ETHERNET_BROADCAST_MAC); } void net_arp_handle(struct arp_header * packet, fs_node_t * nic) { printf("net: arp: hardware %d protocol %d operation %d hlen %d plen %d\n", ntohs(packet->arp_htype), ntohs(packet->arp_ptype), ntohs(packet->arp_oper), packet->arp_hlen, packet->arp_plen); struct EthernetDevice * eth_dev = nic->device; if (ntohs(packet->arp_htype) == 1 && ntohs(packet->arp_ptype) == ETHERNET_TYPE_IPV4) { /* Ethernet, IPv4 */ if (packet->arp_data.arp_eth_ipv4.arp_spa) { net_arp_cache_add(eth_dev, packet->arp_data.arp_eth_ipv4.arp_spa, packet->arp_data.arp_eth_ipv4.arp_sha, 0); } if (ntohs(packet->arp_oper) == 1) { char spa[17]; ip_ntoa(ntohl(packet->arp_data.arp_eth_ipv4.arp_spa), spa); char tpa[17]; ip_ntoa(ntohl(packet->arp_data.arp_eth_ipv4.arp_tpa), tpa); printf("net: arp: " MAC_FORMAT " (%s) wants to know who %s is\n", FORMAT_MAC(packet->arp_data.arp_eth_ipv4.arp_sha), spa, tpa); if (eth_dev->ipv4_addr && packet->arp_data.arp_eth_ipv4.arp_tpa == eth_dev->ipv4_addr) { printf("net: arp: that's us, we should reply...\n"); struct arp_header response = {0}; response.arp_htype = htons(1); response.arp_ptype = htons(ETHERNET_TYPE_IPV4); response.arp_hlen = 6; response.arp_plen = 4; response.arp_oper = htons(2); memcpy(response.arp_data.arp_eth_ipv4.arp_sha, eth_dev->mac, 6); memcpy(response.arp_data.arp_eth_ipv4.arp_tha, packet->arp_data.arp_eth_ipv4.arp_sha, 6); response.arp_data.arp_eth_ipv4.arp_spa = eth_dev->ipv4_addr; response.arp_data.arp_eth_ipv4.arp_tpa = packet->arp_data.arp_eth_ipv4.arp_spa; net_eth_send(eth_dev, sizeof(struct arp_header), &response, ETHERNET_TYPE_ARP, packet->arp_data.arp_eth_ipv4.arp_sha); } } else if (ntohs(packet->arp_oper) == 2) { char spa[17]; ip_ntoa(ntohl(packet->arp_data.arp_eth_ipv4.arp_spa), spa); printf("net: arp: " MAC_FORMAT " says they are %s\n", FORMAT_MAC(packet->arp_data.arp_eth_ipv4.arp_sha), spa); } } }