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