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