// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/arp_client.h" #include #include #include #include #include #include "shill/arp_packet.h" #include "shill/logging.h" #include "shill/net/byte_string.h" #include "shill/net/sockets.h" namespace shill { // ARP opcode is the last uint16_t in the ARP header. const size_t ArpClient::kArpOpOffset = sizeof(arphdr) - sizeof(uint16_t); // The largest packet we expect is one with IPv6 addresses in it. const size_t ArpClient::kMaxArpPacketLength = sizeof(arphdr) + sizeof(in6_addr) * 2 + ETH_ALEN * 2; ArpClient::ArpClient(int interface_index) : interface_index_(interface_index), sockets_(new Sockets()), socket_(-1) {} ArpClient::~ArpClient() {} bool ArpClient::StartReplyListener() { return Start(ARPOP_REPLY); } bool ArpClient::StartRequestListener() { return Start(ARPOP_REQUEST); } bool ArpClient::Start(uint16_t arp_opcode) { if (!CreateSocket(arp_opcode)) { LOG(ERROR) << "Could not open ARP socket."; Stop(); return false; } return true; } void ArpClient::Stop() { socket_closer_.reset(); } bool ArpClient::CreateSocket(uint16_t arp_opcode) { int socket = sockets_->Socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_ARP)); if (socket == -1) { PLOG(ERROR) << "Could not create ARP socket"; return false; } socket_ = socket; socket_closer_.reset(new ScopedSocketCloser(sockets_.get(), socket_)); // Create a packet filter incoming ARP packets. const sock_filter arp_filter[] = { // If a packet contains the ARP opcode we are looking for... BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kArpOpOffset), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, arp_opcode, 0, 1), // Return the the packet (up to largest expected packet size). BPF_STMT(BPF_RET | BPF_K, kMaxArpPacketLength), // Otherwise, drop it. BPF_STMT(BPF_RET | BPF_K, 0), }; sock_fprog pf; pf.filter = const_cast(arp_filter); pf.len = arraysize(arp_filter); if (sockets_->AttachFilter(socket_, &pf) != 0) { PLOG(ERROR) << "Could not attach packet filter"; return false; } if (sockets_->SetNonBlocking(socket_) != 0) { PLOG(ERROR) << "Could not set socket to be non-blocking"; return false; } sockaddr_ll socket_address; memset(&socket_address, 0, sizeof(socket_address)); socket_address.sll_family = AF_PACKET; socket_address.sll_protocol = htons(ETHERTYPE_ARP); socket_address.sll_ifindex = interface_index_; if (sockets_->Bind(socket_, reinterpret_cast(&socket_address), sizeof(socket_address)) != 0) { PLOG(ERROR) << "Could not bind socket to interface"; return false; } return true; } bool ArpClient::ReceivePacket(ArpPacket* packet, ByteString* sender) const { ByteString payload(kMaxArpPacketLength); sockaddr_ll socket_address; memset(&socket_address, 0, sizeof(socket_address)); socklen_t socklen = sizeof(socket_address); int result = sockets_->RecvFrom( socket_, payload.GetData(), payload.GetLength(), 0, reinterpret_cast(&socket_address), &socklen); if (result < 0) { PLOG(ERROR) << "Socket recvfrom failed"; return false; } payload.Resize(result); if (!packet->Parse(payload)) { LOG(ERROR) << "Failed to parse ARP packet."; return false; } // The socket address returned may only be big enough to contain // the hardware address of the sender. CHECK(socklen >= sizeof(socket_address) - sizeof(socket_address.sll_addr) + ETH_ALEN); CHECK(socket_address.sll_halen == ETH_ALEN); *sender = ByteString( reinterpret_cast(&socket_address.sll_addr), socket_address.sll_halen); return true; } bool ArpClient::TransmitRequest(const ArpPacket& packet) const { ByteString payload; if (!packet.FormatRequest(&payload)) { return false; } sockaddr_ll socket_address; memset(&socket_address, 0, sizeof(socket_address)); socket_address.sll_family = AF_PACKET; socket_address.sll_protocol = htons(ETHERTYPE_ARP); socket_address.sll_hatype = ARPHRD_ETHER; socket_address.sll_halen = ETH_ALEN; socket_address.sll_ifindex = interface_index_; ByteString remote_address = packet.remote_mac_address(); CHECK(sizeof(socket_address.sll_addr) >= remote_address.GetLength()); if (remote_address.IsZero()) { // If the destination MAC address is unspecified, send the packet // to the broadcast (all-ones) address. remote_address.BitwiseInvert(); } memcpy(&socket_address.sll_addr, remote_address.GetConstData(), remote_address.GetLength()); int result = sockets_->SendTo( socket_, payload.GetConstData(), payload.GetLength(), 0, reinterpret_cast(&socket_address), sizeof(socket_address)); const int expected_result = static_cast(payload.GetLength()); if (result != expected_result) { if (result < 0) { PLOG(ERROR) << "Socket sendto failed"; } else if (result < static_cast(payload.GetLength())) { LOG(ERROR) << "Socket sendto returned " << result << " which is different from expected result " << expected_result; } return false; } return true; } } // namespace shill