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 23ARPService::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 34ARPService::~ARPService() 35{ 36 if (fEthernet) 37 fEthernet->UnregisterEthernetSubService(this); 38} 39 40// Init 41status_t 42ARPService::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 52uint16 53ARPService::EthernetProtocol() const 54{ 55 return ETHERTYPE_ARP; 56} 57 58// HandleEthernetPacket 59void 60ARPService::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 112status_t 113ARPService::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 164status_t 165ARPService::_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 184ARPService::MapEntry * 185ARPService::_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 199void 200ARPService::_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