/* * Copyright 2005, Ingo Weinhold . * All rights reserved. Distributed under the terms of the MIT License. */ #include #include #include #include //#define TRACE_ARP #ifdef TRACE_ARP # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif // constructor ARPService::ARPService(EthernetService *ethernet) : EthernetSubService(kARPServiceName), fEthernet(ethernet), fAge(0) { // clear table for (int i = 0; i < MAP_ENTRY_COUNT; i++) fEntries[i].ip = INADDR_ANY; } // destructor ARPService::~ARPService() { if (fEthernet) fEthernet->UnregisterEthernetSubService(this); } // Init status_t ARPService::Init() { if (!fEthernet) return B_BAD_VALUE; if (!fEthernet->RegisterEthernetSubService(this)) return B_NO_MEMORY; return B_OK; } // EthernetProtocol uint16 ARPService::EthernetProtocol() const { return ETHERTYPE_ARP; } // HandleEthernetPacket void ARPService::HandleEthernetPacket(EthernetService *ethernet, const mac_addr_t &targetAddress, const void *data, size_t size) { TRACE(("ARPService::HandleEthernetPacket(): %lu - %lu bytes\n", size, sizeof(ip_header))); if (size < sizeof(arp_header)) return; arp_header *header = (arp_header*)data; // check packet validity if (header->hardware_format != htons(ARPHRD_ETHER) || header->protocol_format != htons(ETHERTYPE_IP) || header->hardware_length != sizeof(mac_addr_t) || header->protocol_length != sizeof(ip_addr_t) // valid sender MAC? || header->sender_mac == kNoMACAddress || header->sender_mac == kBroadcastMACAddress // do we support the opcode? || (header->opcode != htons(ARPOP_REQUEST) && header->opcode != htons(ARPOP_REPLY))) { return; } // if this is a request, we continue only, if we have the targeted IP if (header->opcode == htons(ARPOP_REQUEST) && (fEthernet->IPAddress() == INADDR_ANY || header->target_ip != htonl(fEthernet->IPAddress()))) { return; } // if this is a reqly, we accept it only, if it was directly sent to us if (header->opcode == htons(ARPOP_REPLY) && (targetAddress != fEthernet->MACAddress() || header->target_mac != targetAddress)) { return; } // if sender IP looks valid, enter the mapping if (header->sender_ip != htonl(INADDR_ANY) && header->sender_ip != htonl(INADDR_BROADCAST)) { _PutEntry(ntohl(header->sender_ip), header->sender_mac); } // if this is a request, send a reply if (header->opcode == htons(ARPOP_REQUEST)) { _SendARPPacket(ntohl(header->sender_ip), header->sender_mac, ARPOP_REPLY); } } // GetMACForIP status_t ARPService::GetMACForIP(ip_addr_t ip, mac_addr_t &mac) { TRACE(("ARPService::GetMACForIP(%08lx)\n", ip)); if (ip == INADDR_ANY) return B_BAD_VALUE; if (ip == INADDR_BROADCAST) { mac = kBroadcastMACAddress; TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip, mac.ToUInt64())); return B_OK; } // already known? if (MapEntry *entry = _FindEntry(ip)) { mac = entry->mac; TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip, mac.ToUInt64())); return B_OK; } for (int i = 0; i < ARP_REQUEST_RETRY_COUNT; i++) { // send request status_t error = _SendARPPacket(ip, kBroadcastMACAddress, ARPOP_REQUEST); if (error != B_OK) { TRACE(("ARPService::GetMACForIP(%08lx) failed: sending failed\n", ip)); return error; } bigtime_t startTime = system_time(); do { fEthernet->ProcessIncomingPackets(); // received reply? if (MapEntry *entry = _FindEntry(ip)) { mac = entry->mac; TRACE(("ARPService::GetMACForIP(%08lx) done: %012llx\n", ip, mac.ToUInt64())); return B_OK; } } while (system_time() - startTime < ARP_REPLY_TIMEOUT); } TRACE(("ARPService::GetMACForIP(%08lx) failed: no reply\n", ip)); return EHOSTUNREACH; } // _SendARPPacket status_t ARPService::_SendARPPacket(ip_addr_t ip, const mac_addr_t &mac, uint16 opcode) { // prepare ARP header arp_header header; ChainBuffer headerBuffer(&header, sizeof(header)); header.hardware_format = htons(ARPHRD_ETHER); header.protocol_format = htons(ETHERTYPE_IP); header.hardware_length = sizeof(mac_addr_t); header.protocol_length = sizeof(ip_addr_t); header.opcode = htons(opcode); header.sender_mac = fEthernet->MACAddress(); header.sender_ip = htonl(fEthernet->IPAddress()); header.target_mac = (mac == kBroadcastMACAddress ? kNoMACAddress : mac); header.target_ip = htonl(ip); return fEthernet->Send(mac, ETHERTYPE_ARP, &headerBuffer); } // _FindEntry ARPService::MapEntry * ARPService::_FindEntry(ip_addr_t ip) { if (ip == INADDR_ANY) return NULL; for (int i = 0; i < MAP_ENTRY_COUNT; i++) { if (ip == fEntries[i].ip) return fEntries + i; } return NULL; } // _PutEntry void ARPService::_PutEntry(ip_addr_t ip, const mac_addr_t &mac) { // find empty/oldest slot MapEntry *entry = fEntries; for (int i = 0; i < MAP_ENTRY_COUNT; i++) { if (fEntries[i].ip == INADDR_ANY) { entry = fEntries + i; break; } if (fAge - fEntries[i].age > fAge - entry->age) entry = fEntries + i; } entry->age = fAge++; entry->ip = ip; entry->mac = mac; }