1 /*
2  * Copyright (c) 2014 Chris Anderson
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include "minip-internal.h"
10 
11 #include <lk/list.h>
12 #include <string.h>
13 #include <malloc.h>
14 #include <stdio.h>
15 #include <kernel/thread.h>
16 #include <kernel/mutex.h>
17 #include <lk/trace.h>
18 
19 typedef union {
20     uint32_t u;
21     uint8_t b[4];
22 } ipv4_t;
23 
24 #define LOCAL_TRACE 0
25 static struct list_node arp_list;
26 typedef struct {
27     struct list_node node;
28     uint32_t addr;
29     uint8_t mac[6];
30 } arp_entry_t;
31 
32 static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex);
33 
arp_cache_init(void)34 void arp_cache_init(void) {
35     list_initialize(&arp_list);
36 }
37 
mru_update(struct list_node * entry)38 static inline void mru_update(struct list_node *entry) {
39     if (arp_list.next == entry)
40         return;
41 
42     list_delete(entry);
43     list_add_head(&arp_list, entry);
44 }
45 
arp_cache_update(uint32_t addr,const uint8_t mac[6])46 void arp_cache_update(uint32_t addr, const uint8_t mac[6]) {
47     arp_entry_t *arp;
48     ipv4_t ip;
49     bool found = false;
50 
51     ip.u = addr;
52 
53     // Ignore 0.0.0.0 or x.x.x.255
54     if (ip.u == 0 || ip.b[3] == 0xFF) {
55         return;
56     }
57 
58     /* If the entry is in the cache update the address and move
59      * it to head */
60     mutex_acquire(&arp_mutex);
61     list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
62         if (arp->addr == addr) {
63             arp->addr = addr;
64             mru_update(&arp->node);
65             found = true;
66             break;
67         }
68     }
69 
70     if (!found) {
71         LTRACEF("Adding %u.%u.%u.%u -> %02x:%02x:%02x:%02x:%02x:%02x to cache\n",
72                 ip.b[0], ip.b[1], ip.b[2], ip.b[3],
73                 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
74         arp = malloc(sizeof(arp_entry_t));
75         if (arp == NULL) {
76             goto err;
77         }
78 
79         arp->addr = addr;
80         memcpy(arp->mac, mac, sizeof(arp->mac));
81         list_add_head(&arp_list, &arp->node);
82     }
83 
84 err:
85     mutex_release(&arp_mutex);
86     return;
87 }
88 
89 /* Looks up and returns a MAC address based on the provided ip addr */
arp_cache_lookup(uint32_t addr)90 uint8_t *arp_cache_lookup(uint32_t addr) {
91     arp_entry_t *arp = NULL;
92     uint8_t *ret = NULL;
93 
94     /* If the entry is in the cache update the address and move
95      * it to head */
96     mutex_acquire(&arp_mutex);
97     list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
98         if (arp->addr == addr) {
99             mru_update(&arp->node);
100             ret = arp->mac;
101             break;
102         }
103     }
104     mutex_release(&arp_mutex);
105 
106     return ret;
107 }
108 
arp_cache_dump(void)109 void arp_cache_dump(void) {
110     int i = 0;
111     arp_entry_t *arp;
112 
113     if (!list_is_empty(&arp_list)) {
114         list_for_every_entry(&arp_list, arp, arp_entry_t, node) {
115             ipv4_t ip;
116             ip.u = arp->addr;
117             printf("%2d: %u.%u.%u.%u -> %02x:%02x:%02x:%02x:%02x:%02x\n",
118                    i++, ip.b[0], ip.b[1], ip.b[2], ip.b[3],
119                    arp->mac[0], arp->mac[1], arp->mac[2], arp->mac[3], arp->mac[4], arp->mac[5]);
120         }
121     } else {
122         printf("The arp table is empty\n");
123     }
124 }
125 
arp_send_request(uint32_t addr)126 int arp_send_request(uint32_t addr) {
127     pktbuf_t *p;
128     struct eth_hdr *eth;
129     struct arp_pkt *arp;
130 
131     if ((p = pktbuf_alloc()) == NULL) {
132         return -1;
133     }
134 
135     eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
136     arp = pktbuf_append(p, sizeof(struct arp_pkt));
137     minip_build_mac_hdr(eth, bcast_mac, ETH_TYPE_ARP);
138 
139     arp->htype = htons(0x0001);
140     arp->ptype = htons(0x0800);
141     arp->hlen = 6;
142     arp->plen = 4;
143     arp->oper = htons(ARP_OPER_REQUEST);
144     arp->spa = minip_get_ipaddr();
145     arp->tpa = addr;
146     minip_get_macaddr(arp->sha);
147     mac_addr_copy(arp->tha, bcast_mac);
148 
149     minip_tx_handler(p);
150     return 0;
151 }
152 
handle_arp_timeout_cb(void * arg)153 static void handle_arp_timeout_cb(void *arg) {
154     *(bool *)arg = true;
155 }
156 
arp_get_dest_mac(uint32_t host)157 const uint8_t *arp_get_dest_mac(uint32_t host) {
158     const uint8_t *dst_mac = NULL;
159     bool arp_timeout = false;
160     net_timer_t arp_timeout_timer;
161 
162     if (host == IPV4_BCAST) {
163         return bcast_mac;
164     }
165 
166     dst_mac = arp_cache_lookup(host);
167     if (dst_mac == NULL) {
168         arp_send_request(host);
169         memset(&arp_timeout_timer, 0, sizeof(arp_timeout_timer));
170         net_timer_set(&arp_timeout_timer, handle_arp_timeout_cb, &arp_timeout, 100);
171         while (!arp_timeout) {
172             dst_mac = arp_cache_lookup(host);
173             if (dst_mac) {
174                 net_timer_cancel(&arp_timeout_timer);
175                 break;
176             }
177         }
178     }
179 
180     return dst_mac;
181 }
182