Files
lk/lib/minip/arp.c
Travis Geiselbrecht 10853d3259 WIP more net stack
2022-04-04 00:49:58 -07:00

248 lines
6.3 KiB
C

/*
* Copyright (c) 2014 Chris Anderson
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "minip-internal.h"
#include <lk/list.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <lk/trace.h>
#define LOCAL_TRACE 0
typedef struct {
struct list_node node;
uint32_t addr;
uint8_t mac[6];
} arp_entry_t;
static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex);
void arp_cache_init(void) {}
static void mru_update(struct list_node *entry) {
if (arp_list.next == entry)
return;
list_delete(entry);
list_add_head(&arp_list, entry);
}
void arp_cache_update(uint32_t addr, const uint8_t mac[6]) {
arp_entry_t *arp;
ipv4_t ip;
bool found = false;
ip.u = addr;
// Ignore 0.0.0.0 or x.x.x.255
if (ip.u == 0 || ip.b[3] == 0xFF) {
return;
}
/* If the entry is in the cache update the address and move
* it to head */
mutex_acquire(&arp_mutex);
list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
if (arp->addr == addr) {
arp->addr = addr;
mru_update(&arp->node);
found = true;
break;
}
}
if (!found) {
LTRACEF("Adding %u.%u.%u.%u -> %02x:%02x:%02x:%02x:%02x:%02x to cache\n",
ip.b[0], ip.b[1], ip.b[2], ip.b[3],
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
arp = malloc(sizeof(arp_entry_t));
if (arp == NULL) {
goto err;
}
arp->addr = addr;
mac_addr_copy(arp->mac, mac);
list_add_head(&arp_list, &arp->node);
}
err:
mutex_release(&arp_mutex);
return;
}
/* Looks up and returns a MAC address based on the provided ip addr */
uint8_t *arp_cache_lookup(uint32_t addr) {
arp_entry_t *arp = NULL;
uint8_t *ret = NULL;
/* If the entry is in the cache update the address and move
* it to head */
mutex_acquire(&arp_mutex);
list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
if (arp->addr == addr) {
mru_update(&arp->node);
ret = arp->mac;
break;
}
}
mutex_release(&arp_mutex);
return ret;
}
void arp_cache_dump(void) {
int i = 0;
arp_entry_t *arp;
if (!list_is_empty(&arp_list)) {
list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
ipv4_t ip;
ip.u = arp->addr;
printf("%2d: %u.%u.%u.%u -> %02x:%02x:%02x:%02x:%02x:%02x\n",
i++, ip.b[0], ip.b[1], ip.b[2], ip.b[3],
arp->mac[0], arp->mac[1], arp->mac[2], arp->mac[3], arp->mac[4], arp->mac[5]);
}
} else {
printf("The arp table is empty\n");
}
}
int arp_send_request(netif_t *netif, ipv4_addr_t addr) {
pktbuf_t *p;
struct eth_hdr *eth;
struct arp_pkt *arp;
if ((p = pktbuf_alloc()) == NULL) {
return -1;
}
eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
arp = pktbuf_append(p, sizeof(struct arp_pkt));
minip_build_mac_hdr(netif, eth, bcast_mac, ETH_TYPE_ARP);
arp->htype = htons(0x0001);
arp->ptype = htons(0x0800);
arp->hlen = 6;
arp->plen = 4;
arp->oper = htons(ARP_OPER_REQUEST);
arp->spa = netif->ipv4_addr;
arp->tpa = addr;
mac_addr_copy(arp->sha, netif->mac_address);
mac_addr_copy(arp->tha, bcast_mac);
if (netif->tx_func) {
netif->tx_func(netif->tx_func_arg, p);
}
return 0;
}
static void handle_arp_timeout_cb(void *arg) {
*(bool *)arg = true;
}
const uint8_t *arp_get_dest_mac(uint32_t host) {
const uint8_t *dst_mac = NULL;
bool arp_timeout = false;
net_timer_t arp_timeout_timer;
if (host == IPV4_BCAST) {
return bcast_mac;
}
dst_mac = arp_cache_lookup(host);
if (dst_mac == NULL) {
// look up route to set local address
ipv4_route_t *route = ipv4_search_route(host);
if (!route) {
return NULL;
}
netif_t *netif = route->interface;
arp_send_request(netif, host);
memset(&arp_timeout_timer, 0, sizeof(arp_timeout_timer));
net_timer_set(&arp_timeout_timer, handle_arp_timeout_cb, &arp_timeout, 100);
while (!arp_timeout) {
dst_mac = arp_cache_lookup(host);
if (dst_mac) {
net_timer_cancel(&arp_timeout_timer);
break;
}
}
ipv4_dec_route_ref(route);
}
return dst_mac;
}
int handle_arp_pkt(netif_t *netif, pktbuf_t *p) {
struct eth_hdr *eth;
struct arp_pkt *arp;
LTRACEF("ARP packet, len %u\n", p->dlen);
eth = (void *) (p->data - sizeof(struct eth_hdr));
if ((arp = pktbuf_consume(p, sizeof(struct arp_pkt))) == NULL) {
return -1;
}
switch (ntohs(arp->oper)) {
case ARP_OPER_REQUEST: {
pktbuf_t *rp;
struct eth_hdr *reth;
struct arp_pkt *rarp;
if (memcmp(&arp->tpa, &netif->ipv4_addr, sizeof(netif->ipv4_addr)) == 0) {
if ((rp = pktbuf_alloc()) == NULL) {
break;
}
LTRACEF("arp request for us\n");
reth = pktbuf_prepend(rp, sizeof(struct eth_hdr));
rarp = pktbuf_append(rp, sizeof(struct arp_pkt));
// Eth header
minip_build_mac_hdr(netif, reth, eth->src_mac, ETH_TYPE_ARP);
// ARP packet
rarp->oper = htons(ARP_OPER_REPLY);
rarp->htype = htons(0x0001);
rarp->ptype = htons(0x0800);
rarp->hlen = 6;
rarp->plen = 4;
mac_addr_copy(rarp->sha, netif->mac_address);
rarp->spa = netif->ipv4_addr;
mac_addr_copy(rarp->tha, arp->sha);
rarp->tpa = arp->spa;
netif->tx_func(netif->tx_func_arg, rp);
}
}
break;
case ARP_OPER_REPLY: {
LTRACEF("arp reply for us\n");
uint32_t addr;
memcpy(&addr, &arp->spa, sizeof(addr)); // unaligned word
arp_cache_update(addr, arp->sha);
}
break;
}
return 0;
}