arp_packet.cc revision ac1328e5143f6ee0054d5cb2f7d17754c16a3814
1// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "shill/arp_packet.h" 6 7#include <net/ethernet.h> 8#include <net/if_arp.h> 9#include <netinet/in.h> 10#include <string.h> 11 12#include <base/logging.h> 13 14namespace shill { 15 16const size_t ArpPacket::kMinPayloadSize = ETH_ZLEN - ETH_HLEN; 17 18ArpPacket::ArpPacket() 19 : local_ip_address_(IPAddress::kFamilyUnknown), 20 remote_ip_address_(IPAddress::kFamilyUnknown) {} 21 22ArpPacket::ArpPacket( 23 const IPAddress &local_ip, const IPAddress &remote_ip, 24 const ByteString &local_mac, const ByteString &remote_mac) 25 : local_ip_address_(local_ip), 26 remote_ip_address_(remote_ip), 27 local_mac_address_(local_mac), 28 remote_mac_address_(remote_mac) {} 29 30ArpPacket::~ArpPacket() {} 31 32// Format of an ARP packet (all multi-byte values are big-endian): 33// 34// Byte 0 Byte 1 Byte 2 Byte 3 35// +-----------------+-----------------+-----------------+-----------------+ 36// | Format of hardware address (ether)| Format of Protocol Address (IP) | 37// +-----------------+-----------------+-----------------------------------+ 38// | Hardware Length | Protocol Length | ARP Protocol OpCode | 39// +-----------------+-----------------+-----------------------------------+ 40// 41// plus a variable length section... 42// 43// +-----------------------------------------------------------------------+ 44// | Sender Hardware Address (of length "Hardware Length")... | 45// +-----------------------------------------------------------------------+ 46// | Sender IP Address (of length "Protocol Length")... | 47// +-----------------------------------------------------------------------+ 48// | Target Hardware Address (of length "Hardware Length")... | 49// +-----------------------------------------------------------------------+ 50// | Target IP Address (of length "Protocol Length")... | 51// +-----------------------------------------------------------------------+ 52bool ArpPacket::ParseReply(const ByteString &packet) { 53 arphdr header; 54 if (packet.GetLength() < sizeof(header)) { 55 LOG(ERROR) << "Packet size " << packet.GetLength() 56 << " is too short to contain ARP header."; 57 return false; 58 } 59 60 memcpy(&header, packet.GetConstData(), sizeof(header)); 61 62 const uint16 hardware_type = ntohs(header.ar_hrd); 63 if (hardware_type != ARPHRD_ETHER) { 64 NOTIMPLEMENTED() << "Packet is of unknown ARPHRD type " 65 << hardware_type; 66 return false; 67 } 68 const uint16 protocol = ntohs(header.ar_pro); 69 IPAddress::Family family = IPAddress::kFamilyUnknown; 70 if (protocol == ETHERTYPE_IP) { 71 family = IPAddress::kFamilyIPv4; 72 } else if (protocol == ETHERTYPE_IPV6) { 73 family = IPAddress::kFamilyIPv6; 74 } else { 75 NOTIMPLEMENTED() << "Packet has unknown protocol " 76 << protocol; 77 return false; 78 } 79 if (header.ar_hln != ETH_ALEN) { 80 LOG(ERROR) << "Packet has unexpected hardware address length " 81 << static_cast<int>(header.ar_hln) << "; expected " << ETH_ALEN; 82 return false; 83 } 84 size_t ip_address_length = IPAddress::GetAddressLength(family); 85 if (header.ar_pln != ip_address_length) { 86 LOG(ERROR) << "Packet has unexpected protocol address length " 87 << static_cast<int>(header.ar_hln) << "; expected " 88 << ip_address_length; 89 return false; 90 } 91 const uint16 operation = ntohs(header.ar_op); 92 if (operation != ARPOP_REPLY) { 93 NOTIMPLEMENTED() << "Packet is not an ARP reply but of type " 94 << operation; 95 return false; 96 } 97 size_t min_packet_size = 98 sizeof(header) + 2 * ip_address_length + 2 * ETH_ALEN; 99 if (packet.GetLength() < min_packet_size) { 100 NOTIMPLEMENTED() << "Packet of size " 101 << packet.GetLength() 102 << " is too small to contain entire ARP payload; " 103 << "expected at least " 104 << min_packet_size; 105 return false; 106 } 107 local_mac_address_ = packet.GetSubstring(sizeof(header), ETH_ALEN); 108 local_ip_address_ = IPAddress(family, packet.GetSubstring( 109 sizeof(header) + ETH_ALEN, ip_address_length)); 110 remote_mac_address_ = packet.GetSubstring( 111 sizeof(header) + ETH_ALEN + ip_address_length, ETH_ALEN); 112 remote_ip_address_ = IPAddress(family, packet.GetSubstring( 113 sizeof(header) + ETH_ALEN * 2 + ip_address_length, ip_address_length)); 114 return true; 115} 116 117// Output a payload from local parameters. 118bool ArpPacket::FormatRequest(ByteString *packet) const { 119 if (!local_ip_address_.IsValid() || !remote_ip_address_.IsValid()) { 120 LOG(ERROR) << "Local or remote IP address is not valid."; 121 return false; 122 } 123 if (local_ip_address_.family() != remote_ip_address_.family()) { 124 LOG(ERROR) << "Local and remote IP address families do not match!"; 125 return false; 126 } 127 uint16 protocol; 128 IPAddress::Family family = local_ip_address_.family(); 129 if (family == IPAddress::kFamilyIPv4) { 130 protocol = ETHERTYPE_IP; 131 } else if (family == IPAddress::kFamilyIPv6) { 132 protocol = ETHERTYPE_IPV6; 133 } else { 134 NOTIMPLEMENTED() << "Address family " 135 << IPAddress::GetAddressFamilyName(family) 136 << " is not supported."; 137 return false; 138 } 139 size_t ip_address_length = IPAddress::GetAddressLength(family); 140 CHECK(ip_address_length < kuint8max); 141 if (local_mac_address_.GetLength() != ETH_ALEN || 142 remote_mac_address_.GetLength() != ETH_ALEN) { 143 LOG(ERROR) << "Local or remote MAC address length is incorrect."; 144 return false; 145 } 146 147 arphdr header; 148 header.ar_hrd = htons(ARPHRD_ETHER); 149 header.ar_pro = htons(protocol); 150 header.ar_hln = ETH_ALEN; 151 header.ar_pln = ip_address_length; 152 header.ar_op = htons(ARPOP_REQUEST); 153 154 *packet = ByteString(reinterpret_cast<const unsigned char *>(&header), 155 sizeof(header)); 156 157 packet->Append(local_mac_address_); 158 packet->Append(local_ip_address_.address()); 159 packet->Append(remote_mac_address_); 160 packet->Append(remote_ip_address_.address()); 161 162 if (packet->GetLength() < kMinPayloadSize) { 163 packet->Append(ByteString(kMinPayloadSize - packet->GetLength())); 164 } 165 166 return true; 167} 168 169} // namespace shill 170