blob: 2f0df2c21f3f1c70b5220c5610b162540eb65676 [file] [log] [blame]
Peter Qiuc0beca52015-09-03 11:25:46 -07001//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Paul Stewart91a5aac2012-07-20 11:55:40 -070016
17#include "shill/arp_packet.h"
18
19#include <net/ethernet.h>
20#include <net/if_arp.h>
21#include <netinet/in.h>
22#include <string.h>
23
Christopher Wileyb691efd2012-08-09 13:51:51 -070024#include "shill/logging.h"
Paul Stewart91a5aac2012-07-20 11:55:40 -070025
26namespace shill {
27
Paul Stewartac1328e2012-07-20 11:55:40 -070028const size_t ArpPacket::kMinPayloadSize = ETH_ZLEN - ETH_HLEN;
29
Paul Stewart91a5aac2012-07-20 11:55:40 -070030ArpPacket::ArpPacket()
Paul Stewart417e5f02014-10-09 08:52:35 -070031 : operation_(0),
32 local_ip_address_(IPAddress::kFamilyUnknown),
Paul Stewart91a5aac2012-07-20 11:55:40 -070033 remote_ip_address_(IPAddress::kFamilyUnknown) {}
34
35ArpPacket::ArpPacket(
Paul Stewarta794cd62015-06-16 13:13:10 -070036 const IPAddress& local_ip, const IPAddress& remote_ip,
37 const ByteString& local_mac, const ByteString& remote_mac)
Paul Stewart91a5aac2012-07-20 11:55:40 -070038 : local_ip_address_(local_ip),
39 remote_ip_address_(remote_ip),
40 local_mac_address_(local_mac),
41 remote_mac_address_(remote_mac) {}
42
43ArpPacket::~ArpPacket() {}
44
45// Format of an ARP packet (all multi-byte values are big-endian):
46//
47// Byte 0 Byte 1 Byte 2 Byte 3
48// +-----------------+-----------------+-----------------+-----------------+
49// | Format of hardware address (ether)| Format of Protocol Address (IP) |
50// +-----------------+-----------------+-----------------------------------+
51// | Hardware Length | Protocol Length | ARP Protocol OpCode |
52// +-----------------+-----------------+-----------------------------------+
53//
54// plus a variable length section...
55//
56// +-----------------------------------------------------------------------+
57// | Sender Hardware Address (of length "Hardware Length")... |
58// +-----------------------------------------------------------------------+
59// | Sender IP Address (of length "Protocol Length")... |
60// +-----------------------------------------------------------------------+
61// | Target Hardware Address (of length "Hardware Length")... |
62// +-----------------------------------------------------------------------+
63// | Target IP Address (of length "Protocol Length")... |
64// +-----------------------------------------------------------------------+
Paul Stewarta794cd62015-06-16 13:13:10 -070065bool ArpPacket::Parse(const ByteString& packet) {
Paul Stewart91a5aac2012-07-20 11:55:40 -070066 arphdr header;
67 if (packet.GetLength() < sizeof(header)) {
68 LOG(ERROR) << "Packet size " << packet.GetLength()
69 << " is too short to contain ARP header.";
70 return false;
71 }
72
73 memcpy(&header, packet.GetConstData(), sizeof(header));
74
Ben Chan7fab8972014-08-10 17:14:46 -070075 const uint16_t hardware_type = ntohs(header.ar_hrd);
Paul Stewart91a5aac2012-07-20 11:55:40 -070076 if (hardware_type != ARPHRD_ETHER) {
77 NOTIMPLEMENTED() << "Packet is of unknown ARPHRD type "
78 << hardware_type;
79 return false;
80 }
Ben Chan7fab8972014-08-10 17:14:46 -070081 const uint16_t protocol = ntohs(header.ar_pro);
Paul Stewart91a5aac2012-07-20 11:55:40 -070082 IPAddress::Family family = IPAddress::kFamilyUnknown;
83 if (protocol == ETHERTYPE_IP) {
84 family = IPAddress::kFamilyIPv4;
85 } else if (protocol == ETHERTYPE_IPV6) {
86 family = IPAddress::kFamilyIPv6;
87 } else {
88 NOTIMPLEMENTED() << "Packet has unknown protocol "
89 << protocol;
90 return false;
91 }
92 if (header.ar_hln != ETH_ALEN) {
93 LOG(ERROR) << "Packet has unexpected hardware address length "
94 << static_cast<int>(header.ar_hln) << "; expected " << ETH_ALEN;
95 return false;
96 }
97 size_t ip_address_length = IPAddress::GetAddressLength(family);
98 if (header.ar_pln != ip_address_length) {
99 LOG(ERROR) << "Packet has unexpected protocol address length "
100 << static_cast<int>(header.ar_hln) << "; expected "
101 << ip_address_length;
102 return false;
103 }
Ben Chan7fab8972014-08-10 17:14:46 -0700104 const uint16_t operation = ntohs(header.ar_op);
Paul Stewart417e5f02014-10-09 08:52:35 -0700105 if (operation != ARPOP_REPLY && operation != ARPOP_REQUEST) {
106 NOTIMPLEMENTED() << "Packet is not an ARP reply or request but of type "
Paul Stewart91a5aac2012-07-20 11:55:40 -0700107 << operation;
108 return false;
109 }
110 size_t min_packet_size =
111 sizeof(header) + 2 * ip_address_length + 2 * ETH_ALEN;
112 if (packet.GetLength() < min_packet_size) {
113 NOTIMPLEMENTED() << "Packet of size "
114 << packet.GetLength()
115 << " is too small to contain entire ARP payload; "
116 << "expected at least "
117 << min_packet_size;
118 return false;
119 }
Paul Stewart417e5f02014-10-09 08:52:35 -0700120 operation_ = operation;
Paul Stewart91a5aac2012-07-20 11:55:40 -0700121 local_mac_address_ = packet.GetSubstring(sizeof(header), ETH_ALEN);
122 local_ip_address_ = IPAddress(family, packet.GetSubstring(
123 sizeof(header) + ETH_ALEN, ip_address_length));
124 remote_mac_address_ = packet.GetSubstring(
125 sizeof(header) + ETH_ALEN + ip_address_length, ETH_ALEN);
126 remote_ip_address_ = IPAddress(family, packet.GetSubstring(
127 sizeof(header) + ETH_ALEN * 2 + ip_address_length, ip_address_length));
128 return true;
129}
130
131// Output a payload from local parameters.
Paul Stewarta794cd62015-06-16 13:13:10 -0700132bool ArpPacket::FormatRequest(ByteString* packet) const {
Paul Stewart91a5aac2012-07-20 11:55:40 -0700133 if (!local_ip_address_.IsValid() || !remote_ip_address_.IsValid()) {
134 LOG(ERROR) << "Local or remote IP address is not valid.";
135 return false;
136 }
137 if (local_ip_address_.family() != remote_ip_address_.family()) {
138 LOG(ERROR) << "Local and remote IP address families do not match!";
139 return false;
140 }
Ben Chan7fab8972014-08-10 17:14:46 -0700141 uint16_t protocol;
Paul Stewart91a5aac2012-07-20 11:55:40 -0700142 IPAddress::Family family = local_ip_address_.family();
143 if (family == IPAddress::kFamilyIPv4) {
144 protocol = ETHERTYPE_IP;
145 } else if (family == IPAddress::kFamilyIPv6) {
146 protocol = ETHERTYPE_IPV6;
147 } else {
148 NOTIMPLEMENTED() << "Address family "
149 << IPAddress::GetAddressFamilyName(family)
150 << " is not supported.";
151 return false;
152 }
153 size_t ip_address_length = IPAddress::GetAddressLength(family);
154 CHECK(ip_address_length < kuint8max);
155 if (local_mac_address_.GetLength() != ETH_ALEN ||
156 remote_mac_address_.GetLength() != ETH_ALEN) {
157 LOG(ERROR) << "Local or remote MAC address length is incorrect.";
158 return false;
159 }
160
161 arphdr header;
162 header.ar_hrd = htons(ARPHRD_ETHER);
163 header.ar_pro = htons(protocol);
164 header.ar_hln = ETH_ALEN;
165 header.ar_pln = ip_address_length;
166 header.ar_op = htons(ARPOP_REQUEST);
167
Paul Stewarta794cd62015-06-16 13:13:10 -0700168 *packet = ByteString(reinterpret_cast<const unsigned char*>(&header),
Paul Stewart91a5aac2012-07-20 11:55:40 -0700169 sizeof(header));
170
171 packet->Append(local_mac_address_);
172 packet->Append(local_ip_address_.address());
173 packet->Append(remote_mac_address_);
174 packet->Append(remote_ip_address_.address());
175
Paul Stewartac1328e2012-07-20 11:55:40 -0700176 if (packet->GetLength() < kMinPayloadSize) {
177 packet->Append(ByteString(kMinPayloadSize - packet->GetLength()));
178 }
179
Paul Stewart91a5aac2012-07-20 11:55:40 -0700180 return true;
181}
182
Paul Stewart417e5f02014-10-09 08:52:35 -0700183bool ArpPacket::IsReply() const {
184 return operation_ == ARPOP_REPLY;
185}
186
Paul Stewart91a5aac2012-07-20 11:55:40 -0700187} // namespace shill