headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / net / ARP.cpp
blob61ffc61546b6351aa2c43b4632e95add8b5f5561
1 /*
2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include <boot/net/ARP.h>
8 #include <stdio.h>
9 #include <KernelExport.h>
11 #include <boot/net/ChainBuffer.h>
14 //#define TRACE_ARP
15 #ifdef TRACE_ARP
16 # define TRACE(x) dprintf x
17 #else
18 # define TRACE(x) ;
19 #endif
22 // constructor
23 ARPService::ARPService(EthernetService *ethernet)
24 : EthernetSubService(kARPServiceName),
25 fEthernet(ethernet),
26 fAge(0)
28 // clear table
29 for (int i = 0; i < MAP_ENTRY_COUNT; i++)
30 fEntries[i].ip = INADDR_ANY;
33 // destructor
34 ARPService::~ARPService()
36 if (fEthernet)
37 fEthernet->UnregisterEthernetSubService(this);
40 // Init
41 status_t
42 ARPService::Init()
44 if (!fEthernet)
45 return B_BAD_VALUE;
46 if (!fEthernet->RegisterEthernetSubService(this))
47 return B_NO_MEMORY;
48 return B_OK;
51 // EthernetProtocol
52 uint16
53 ARPService::EthernetProtocol() const
55 return ETHERTYPE_ARP;
58 // HandleEthernetPacket
59 void
60 ARPService::HandleEthernetPacket(EthernetService *ethernet,
61 const mac_addr_t &targetAddress, const void *data, size_t size)
63 TRACE(("ARPService::HandleEthernetPacket(): %lu - %lu bytes\n", size,
64 sizeof(ip_header)));
66 if (size < sizeof(arp_header))
67 return;
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;
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;
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;
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);
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);
111 // GetMACForIP
112 status_t
113 ARPService::GetMACForIP(ip_addr_t ip, mac_addr_t &mac)
115 TRACE(("ARPService::GetMACForIP(%08lx)\n", ip));
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;
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;
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;
144 bigtime_t startTime = system_time();
145 do {
146 fEthernet->ProcessIncomingPackets();
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;
155 } while (system_time() - startTime < ARP_REPLY_TIMEOUT);
158 TRACE(("ARPService::GetMACForIP(%08lx) failed: no reply\n", ip));
160 return EHOSTUNREACH;
163 // _SendARPPacket
164 status_t
165 ARPService::_SendARPPacket(ip_addr_t ip, const mac_addr_t &mac, uint16 opcode)
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);
180 return fEthernet->Send(mac, ETHERTYPE_ARP, &headerBuffer);
183 // _FindEntry
184 ARPService::MapEntry *
185 ARPService::_FindEntry(ip_addr_t ip)
187 if (ip == INADDR_ANY)
188 return NULL;
190 for (int i = 0; i < MAP_ENTRY_COUNT; i++) {
191 if (ip == fEntries[i].ip)
192 return fEntries + i;
195 return NULL;
198 // _PutEntry
199 void
200 ARPService::_PutEntry(ip_addr_t ip, const mac_addr_t &mac)
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;
210 if (fAge - fEntries[i].age > fAge - entry->age)
211 entry = fEntries + i;
214 entry->age = fAge++;
215 entry->ip = ip;
216 entry->mac = mac;