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