| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/arp_packet.h" |
| |
| #include <net/ethernet.h> |
| #include <net/if_arp.h> |
| #include <netinet/in.h> |
| #include <string.h> |
| |
| #include "shill/logging.h" |
| |
| namespace shill { |
| |
| const size_t ArpPacket::kMinPayloadSize = ETH_ZLEN - ETH_HLEN; |
| |
| ArpPacket::ArpPacket() |
| : operation_(0), |
| local_ip_address_(IPAddress::kFamilyUnknown), |
| remote_ip_address_(IPAddress::kFamilyUnknown) {} |
| |
| ArpPacket::ArpPacket( |
| const IPAddress& local_ip, const IPAddress& remote_ip, |
| const ByteString& local_mac, const ByteString& remote_mac) |
| : local_ip_address_(local_ip), |
| remote_ip_address_(remote_ip), |
| local_mac_address_(local_mac), |
| remote_mac_address_(remote_mac) {} |
| |
| ArpPacket::~ArpPacket() {} |
| |
| // Format of an ARP packet (all multi-byte values are big-endian): |
| // |
| // Byte 0 Byte 1 Byte 2 Byte 3 |
| // +-----------------+-----------------+-----------------+-----------------+ |
| // | Format of hardware address (ether)| Format of Protocol Address (IP) | |
| // +-----------------+-----------------+-----------------------------------+ |
| // | Hardware Length | Protocol Length | ARP Protocol OpCode | |
| // +-----------------+-----------------+-----------------------------------+ |
| // |
| // plus a variable length section... |
| // |
| // +-----------------------------------------------------------------------+ |
| // | Sender Hardware Address (of length "Hardware Length")... | |
| // +-----------------------------------------------------------------------+ |
| // | Sender IP Address (of length "Protocol Length")... | |
| // +-----------------------------------------------------------------------+ |
| // | Target Hardware Address (of length "Hardware Length")... | |
| // +-----------------------------------------------------------------------+ |
| // | Target IP Address (of length "Protocol Length")... | |
| // +-----------------------------------------------------------------------+ |
| bool ArpPacket::Parse(const ByteString& packet) { |
| arphdr header; |
| if (packet.GetLength() < sizeof(header)) { |
| LOG(ERROR) << "Packet size " << packet.GetLength() |
| << " is too short to contain ARP header."; |
| return false; |
| } |
| |
| memcpy(&header, packet.GetConstData(), sizeof(header)); |
| |
| const uint16_t hardware_type = ntohs(header.ar_hrd); |
| if (hardware_type != ARPHRD_ETHER) { |
| NOTIMPLEMENTED() << "Packet is of unknown ARPHRD type " |
| << hardware_type; |
| return false; |
| } |
| const uint16_t protocol = ntohs(header.ar_pro); |
| IPAddress::Family family = IPAddress::kFamilyUnknown; |
| if (protocol == ETHERTYPE_IP) { |
| family = IPAddress::kFamilyIPv4; |
| } else if (protocol == ETHERTYPE_IPV6) { |
| family = IPAddress::kFamilyIPv6; |
| } else { |
| NOTIMPLEMENTED() << "Packet has unknown protocol " |
| << protocol; |
| return false; |
| } |
| if (header.ar_hln != ETH_ALEN) { |
| LOG(ERROR) << "Packet has unexpected hardware address length " |
| << static_cast<int>(header.ar_hln) << "; expected " << ETH_ALEN; |
| return false; |
| } |
| size_t ip_address_length = IPAddress::GetAddressLength(family); |
| if (header.ar_pln != ip_address_length) { |
| LOG(ERROR) << "Packet has unexpected protocol address length " |
| << static_cast<int>(header.ar_hln) << "; expected " |
| << ip_address_length; |
| return false; |
| } |
| const uint16_t operation = ntohs(header.ar_op); |
| if (operation != ARPOP_REPLY && operation != ARPOP_REQUEST) { |
| NOTIMPLEMENTED() << "Packet is not an ARP reply or request but of type " |
| << operation; |
| return false; |
| } |
| size_t min_packet_size = |
| sizeof(header) + 2 * ip_address_length + 2 * ETH_ALEN; |
| if (packet.GetLength() < min_packet_size) { |
| NOTIMPLEMENTED() << "Packet of size " |
| << packet.GetLength() |
| << " is too small to contain entire ARP payload; " |
| << "expected at least " |
| << min_packet_size; |
| return false; |
| } |
| operation_ = operation; |
| local_mac_address_ = packet.GetSubstring(sizeof(header), ETH_ALEN); |
| local_ip_address_ = IPAddress(family, packet.GetSubstring( |
| sizeof(header) + ETH_ALEN, ip_address_length)); |
| remote_mac_address_ = packet.GetSubstring( |
| sizeof(header) + ETH_ALEN + ip_address_length, ETH_ALEN); |
| remote_ip_address_ = IPAddress(family, packet.GetSubstring( |
| sizeof(header) + ETH_ALEN * 2 + ip_address_length, ip_address_length)); |
| return true; |
| } |
| |
| // Output a payload from local parameters. |
| bool ArpPacket::FormatRequest(ByteString* packet) const { |
| if (!local_ip_address_.IsValid() || !remote_ip_address_.IsValid()) { |
| LOG(ERROR) << "Local or remote IP address is not valid."; |
| return false; |
| } |
| if (local_ip_address_.family() != remote_ip_address_.family()) { |
| LOG(ERROR) << "Local and remote IP address families do not match!"; |
| return false; |
| } |
| uint16_t protocol; |
| IPAddress::Family family = local_ip_address_.family(); |
| if (family == IPAddress::kFamilyIPv4) { |
| protocol = ETHERTYPE_IP; |
| } else if (family == IPAddress::kFamilyIPv6) { |
| protocol = ETHERTYPE_IPV6; |
| } else { |
| NOTIMPLEMENTED() << "Address family " |
| << IPAddress::GetAddressFamilyName(family) |
| << " is not supported."; |
| return false; |
| } |
| size_t ip_address_length = IPAddress::GetAddressLength(family); |
| CHECK(ip_address_length < kuint8max); |
| if (local_mac_address_.GetLength() != ETH_ALEN || |
| remote_mac_address_.GetLength() != ETH_ALEN) { |
| LOG(ERROR) << "Local or remote MAC address length is incorrect."; |
| return false; |
| } |
| |
| arphdr header; |
| header.ar_hrd = htons(ARPHRD_ETHER); |
| header.ar_pro = htons(protocol); |
| header.ar_hln = ETH_ALEN; |
| header.ar_pln = ip_address_length; |
| header.ar_op = htons(ARPOP_REQUEST); |
| |
| *packet = ByteString(reinterpret_cast<const unsigned char*>(&header), |
| sizeof(header)); |
| |
| packet->Append(local_mac_address_); |
| packet->Append(local_ip_address_.address()); |
| packet->Append(remote_mac_address_); |
| packet->Append(remote_ip_address_.address()); |
| |
| if (packet->GetLength() < kMinPayloadSize) { |
| packet->Append(ByteString(kMinPayloadSize - packet->GetLength())); |
| } |
| |
| return true; |
| } |
| |
| bool ArpPacket::IsReply() const { |
| return operation_ == ARPOP_REPLY; |
| } |
| |
| } // namespace shill |