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