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 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 34 ARPService::~ARPService() 35 { 36 if (fEthernet) 37 fEthernet->UnregisterEthernetSubService(this); 38 } 39 40 // Init 41 status_t 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 53 ARPService::EthernetProtocol() const 54 { 55 return ETHERTYPE_ARP; 56 } 57 58 // HandleEthernetPacket 59 void 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 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 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 * 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 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