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