1 /*
2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6 #include <boot/net/ARP.h>
7
8 #include <stdio.h>
9 #include <KernelExport.h>
10
11 #include <boot/net/ChainBuffer.h>
12
13
14 //#define TRACE_ARP
15 #ifdef TRACE_ARP
16 # define TRACE(x) dprintf x
17 #else
18 # define TRACE(x) ;
19 #endif
20
21
22 // constructor
ARPService(EthernetService * ethernet)23 ARPService::ARPService(EthernetService *ethernet)
24 : EthernetSubService(kARPServiceName),
25 fEthernet(ethernet),
26 fAge(0)
27 {
28 // clear table
29 for (int i = 0; i < MAP_ENTRY_COUNT; i++)
30 fEntries[i].ip = INADDR_ANY;
31 }
32
33 // destructor
~ARPService()34 ARPService::~ARPService()
35 {
36 if (fEthernet)
37 fEthernet->UnregisterEthernetSubService(this);
38 }
39
40 // Init
41 status_t
Init()42 ARPService::Init()
43 {
44 if (!fEthernet)
45 return B_BAD_VALUE;
46 if (!fEthernet->RegisterEthernetSubService(this))
47 return B_NO_MEMORY;
48 return B_OK;
49 }
50
51 // EthernetProtocol
52 uint16
EthernetProtocol() const53 ARPService::EthernetProtocol() const
54 {
55 return ETHERTYPE_ARP;
56 }
57
58 // HandleEthernetPacket
59 void
HandleEthernetPacket(EthernetService * ethernet,const mac_addr_t & targetAddress,const void * data,size_t size)60 ARPService::HandleEthernetPacket(EthernetService *ethernet,
61 const mac_addr_t &targetAddress, const void *data, size_t size)
62 {
63 TRACE(("ARPService::HandleEthernetPacket(): %lu - %lu bytes\n", size,
64 sizeof(ip_header)));
65
66 if (size < sizeof(arp_header))
67 return;
68
69 arp_header *header = (arp_header*)data;
70 // check packet validity
71 if (header->hardware_format != htons(ARPHRD_ETHER)
72 || header->protocol_format != htons(ETHERTYPE_IP)
73 || header->hardware_length != sizeof(mac_addr_t)
74 || header->protocol_length != sizeof(ip_addr_t)
75 // valid sender MAC?
76 || header->sender_mac == kNoMACAddress
77 || header->sender_mac == kBroadcastMACAddress
78 // do we support the opcode?
79 || (header->opcode != htons(ARPOP_REQUEST)
80 && header->opcode != htons(ARPOP_REPLY))) {
81 return;
82 }
83
84 // if this is a request, we continue only, if we have the targeted IP
85 if (header->opcode == htons(ARPOP_REQUEST)
86 && (fEthernet->IPAddress() == INADDR_ANY
87 || header->target_ip != htonl(fEthernet->IPAddress()))) {
88 return;
89 }
90
91 // if this is a reqly, we accept it only, if it was directly sent to us
92 if (header->opcode == htons(ARPOP_REPLY)
93 && (targetAddress != fEthernet->MACAddress()
94 || header->target_mac != targetAddress)) {
95 return;
96 }
97
98 // if sender IP looks valid, enter the mapping
99 if (header->sender_ip != htonl(INADDR_ANY)
100 && header->sender_ip != htonl(INADDR_BROADCAST)) {
101 _PutEntry(ntohl(header->sender_ip), header->sender_mac);
102 }
103
104 // if this is a request, send a reply
105 if (header->opcode == htons(ARPOP_REQUEST)) {
106 _SendARPPacket(ntohl(header->sender_ip), header->sender_mac,
107 ARPOP_REPLY);
108 }
109 }
110
111 // GetMACForIP
112 status_t
GetMACForIP(ip_addr_t ip,mac_addr_t & mac)113 ARPService::GetMACForIP(ip_addr_t ip, mac_addr_t &mac)
114 {
115 TRACE(("ARPService::GetMACForIP(%08lx)\n", ip));
116
117 if (ip == INADDR_ANY)
118 return B_BAD_VALUE;
119 if (ip == INADDR_BROADCAST) {
120 mac = kBroadcastMACAddress;
121 TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
122 mac.ToUInt64()));
123 return B_OK;
124 }
125
126 // already known?
127 if (MapEntry *entry = _FindEntry(ip)) {
128 mac = entry->mac;
129 TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
130 mac.ToUInt64()));
131 return B_OK;
132 }
133
134 for (int i = 0; i < ARP_REQUEST_RETRY_COUNT; i++) {
135 // send request
136 status_t error = _SendARPPacket(ip, kBroadcastMACAddress,
137 ARPOP_REQUEST);
138 if (error != B_OK) {
139 TRACE(("ARPService::GetMACForIP(%08lx) failed: sending failed\n",
140 ip));
141 return error;
142 }
143
144 bigtime_t startTime = system_time();
145 do {
146 fEthernet->ProcessIncomingPackets();
147
148 // received reply?
149 if (MapEntry *entry = _FindEntry(ip)) {
150 mac = entry->mac;
151 TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip,
152 mac.ToUInt64()));
153 return B_OK;
154 }
155 } while (system_time() - startTime < ARP_REPLY_TIMEOUT);
156 }
157
158 TRACE(("ARPService::GetMACForIP(%08lx) failed: no reply\n", ip));
159
160 return EHOSTUNREACH;
161 }
162
163 // _SendARPPacket
164 status_t
_SendARPPacket(ip_addr_t ip,const mac_addr_t & mac,uint16 opcode)165 ARPService::_SendARPPacket(ip_addr_t ip, const mac_addr_t &mac, uint16 opcode)
166 {
167 // prepare ARP header
168 arp_header header;
169 ChainBuffer headerBuffer(&header, sizeof(header));
170 header.hardware_format = htons(ARPHRD_ETHER);
171 header.protocol_format = htons(ETHERTYPE_IP);
172 header.hardware_length = sizeof(mac_addr_t);
173 header.protocol_length = sizeof(ip_addr_t);
174 header.opcode = htons(opcode);
175 header.sender_mac = fEthernet->MACAddress();
176 header.sender_ip = htonl(fEthernet->IPAddress());
177 header.target_mac = (mac == kBroadcastMACAddress ? kNoMACAddress : mac);
178 header.target_ip = htonl(ip);
179
180 return fEthernet->Send(mac, ETHERTYPE_ARP, &headerBuffer);
181 }
182
183 // _FindEntry
184 ARPService::MapEntry *
_FindEntry(ip_addr_t ip)185 ARPService::_FindEntry(ip_addr_t ip)
186 {
187 if (ip == INADDR_ANY)
188 return NULL;
189
190 for (int i = 0; i < MAP_ENTRY_COUNT; i++) {
191 if (ip == fEntries[i].ip)
192 return fEntries + i;
193 }
194
195 return NULL;
196 }
197
198 // _PutEntry
199 void
_PutEntry(ip_addr_t ip,const mac_addr_t & mac)200 ARPService::_PutEntry(ip_addr_t ip, const mac_addr_t &mac)
201 {
202 // find empty/oldest slot
203 MapEntry *entry = fEntries;
204 for (int i = 0; i < MAP_ENTRY_COUNT; i++) {
205 if (fEntries[i].ip == INADDR_ANY) {
206 entry = fEntries + i;
207 break;
208 }
209
210 if (fAge - fEntries[i].age > fAge - entry->age)
211 entry = fEntries + i;
212 }
213
214 entry->age = fAge++;
215 entry->ip = ip;
216 entry->mac = mac;
217 }
218