blob: 00992fdce54d134895841118a85171a9a07db546 [file] [log] [blame]
Paul Stewart91a5aac2012-07-20 11:55:40 -07001// 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
Christopher Wileyb691efd2012-08-09 13:51:51 -070012#include "shill/logging.h"
Paul Stewart91a5aac2012-07-20 11:55:40 -070013
14namespace shill {
15
Paul Stewartac1328e2012-07-20 11:55:40 -070016const size_t ArpPacket::kMinPayloadSize = ETH_ZLEN - ETH_HLEN;
17
Paul Stewart91a5aac2012-07-20 11:55:40 -070018ArpPacket::ArpPacket()
Paul Stewart417e5f02014-10-09 08:52:35 -070019 : operation_(0),
20 local_ip_address_(IPAddress::kFamilyUnknown),
Paul Stewart91a5aac2012-07-20 11:55:40 -070021 remote_ip_address_(IPAddress::kFamilyUnknown) {}
22
23ArpPacket::ArpPacket(
Paul Stewarta794cd62015-06-16 13:13:10 -070024 const IPAddress& local_ip, const IPAddress& remote_ip,
25 const ByteString& local_mac, const ByteString& remote_mac)
Paul Stewart91a5aac2012-07-20 11:55:40 -070026 : local_ip_address_(local_ip),
27 remote_ip_address_(remote_ip),
28 local_mac_address_(local_mac),
29 remote_mac_address_(remote_mac) {}
30
31ArpPacket::~ArpPacket() {}
32
33// Format of an ARP packet (all multi-byte values are big-endian):
34//
35// Byte 0 Byte 1 Byte 2 Byte 3
36// +-----------------+-----------------+-----------------+-----------------+
37// | Format of hardware address (ether)| Format of Protocol Address (IP) |
38// +-----------------+-----------------+-----------------------------------+
39// | Hardware Length | Protocol Length | ARP Protocol OpCode |
40// +-----------------+-----------------+-----------------------------------+
41//
42// plus a variable length section...
43//
44// +-----------------------------------------------------------------------+
45// | Sender Hardware Address (of length "Hardware Length")... |
46// +-----------------------------------------------------------------------+
47// | Sender IP Address (of length "Protocol Length")... |
48// +-----------------------------------------------------------------------+
49// | Target Hardware Address (of length "Hardware Length")... |
50// +-----------------------------------------------------------------------+
51// | Target IP Address (of length "Protocol Length")... |
52// +-----------------------------------------------------------------------+
Paul Stewarta794cd62015-06-16 13:13:10 -070053bool ArpPacket::Parse(const ByteString& packet) {
Paul Stewart91a5aac2012-07-20 11:55:40 -070054 arphdr header;
55 if (packet.GetLength() < sizeof(header)) {
56 LOG(ERROR) << "Packet size " << packet.GetLength()
57 << " is too short to contain ARP header.";
58 return false;
59 }
60
61 memcpy(&header, packet.GetConstData(), sizeof(header));
62
Ben Chan7fab8972014-08-10 17:14:46 -070063 const uint16_t hardware_type = ntohs(header.ar_hrd);
Paul Stewart91a5aac2012-07-20 11:55:40 -070064 if (hardware_type != ARPHRD_ETHER) {
65 NOTIMPLEMENTED() << "Packet is of unknown ARPHRD type "
66 << hardware_type;
67 return false;
68 }
Ben Chan7fab8972014-08-10 17:14:46 -070069 const uint16_t protocol = ntohs(header.ar_pro);
Paul Stewart91a5aac2012-07-20 11:55:40 -070070 IPAddress::Family family = IPAddress::kFamilyUnknown;
71 if (protocol == ETHERTYPE_IP) {
72 family = IPAddress::kFamilyIPv4;
73 } else if (protocol == ETHERTYPE_IPV6) {
74 family = IPAddress::kFamilyIPv6;
75 } else {
76 NOTIMPLEMENTED() << "Packet has unknown protocol "
77 << protocol;
78 return false;
79 }
80 if (header.ar_hln != ETH_ALEN) {
81 LOG(ERROR) << "Packet has unexpected hardware address length "
82 << static_cast<int>(header.ar_hln) << "; expected " << ETH_ALEN;
83 return false;
84 }
85 size_t ip_address_length = IPAddress::GetAddressLength(family);
86 if (header.ar_pln != ip_address_length) {
87 LOG(ERROR) << "Packet has unexpected protocol address length "
88 << static_cast<int>(header.ar_hln) << "; expected "
89 << ip_address_length;
90 return false;
91 }
Ben Chan7fab8972014-08-10 17:14:46 -070092 const uint16_t operation = ntohs(header.ar_op);
Paul Stewart417e5f02014-10-09 08:52:35 -070093 if (operation != ARPOP_REPLY && operation != ARPOP_REQUEST) {
94 NOTIMPLEMENTED() << "Packet is not an ARP reply or request but of type "
Paul Stewart91a5aac2012-07-20 11:55:40 -070095 << operation;
96 return false;
97 }
98 size_t min_packet_size =
99 sizeof(header) + 2 * ip_address_length + 2 * ETH_ALEN;
100 if (packet.GetLength() < min_packet_size) {
101 NOTIMPLEMENTED() << "Packet of size "
102 << packet.GetLength()
103 << " is too small to contain entire ARP payload; "
104 << "expected at least "
105 << min_packet_size;
106 return false;
107 }
Paul Stewart417e5f02014-10-09 08:52:35 -0700108 operation_ = operation;
Paul Stewart91a5aac2012-07-20 11:55:40 -0700109 local_mac_address_ = packet.GetSubstring(sizeof(header), ETH_ALEN);
110 local_ip_address_ = IPAddress(family, packet.GetSubstring(
111 sizeof(header) + ETH_ALEN, ip_address_length));
112 remote_mac_address_ = packet.GetSubstring(
113 sizeof(header) + ETH_ALEN + ip_address_length, ETH_ALEN);
114 remote_ip_address_ = IPAddress(family, packet.GetSubstring(
115 sizeof(header) + ETH_ALEN * 2 + ip_address_length, ip_address_length));
116 return true;
117}
118
119// Output a payload from local parameters.
Paul Stewarta794cd62015-06-16 13:13:10 -0700120bool ArpPacket::FormatRequest(ByteString* packet) const {
Paul Stewart91a5aac2012-07-20 11:55:40 -0700121 if (!local_ip_address_.IsValid() || !remote_ip_address_.IsValid()) {
122 LOG(ERROR) << "Local or remote IP address is not valid.";
123 return false;
124 }
125 if (local_ip_address_.family() != remote_ip_address_.family()) {
126 LOG(ERROR) << "Local and remote IP address families do not match!";
127 return false;
128 }
Ben Chan7fab8972014-08-10 17:14:46 -0700129 uint16_t protocol;
Paul Stewart91a5aac2012-07-20 11:55:40 -0700130 IPAddress::Family family = local_ip_address_.family();
131 if (family == IPAddress::kFamilyIPv4) {
132 protocol = ETHERTYPE_IP;
133 } else if (family == IPAddress::kFamilyIPv6) {
134 protocol = ETHERTYPE_IPV6;
135 } else {
136 NOTIMPLEMENTED() << "Address family "
137 << IPAddress::GetAddressFamilyName(family)
138 << " is not supported.";
139 return false;
140 }
141 size_t ip_address_length = IPAddress::GetAddressLength(family);
142 CHECK(ip_address_length < kuint8max);
143 if (local_mac_address_.GetLength() != ETH_ALEN ||
144 remote_mac_address_.GetLength() != ETH_ALEN) {
145 LOG(ERROR) << "Local or remote MAC address length is incorrect.";
146 return false;
147 }
148
149 arphdr header;
150 header.ar_hrd = htons(ARPHRD_ETHER);
151 header.ar_pro = htons(protocol);
152 header.ar_hln = ETH_ALEN;
153 header.ar_pln = ip_address_length;
154 header.ar_op = htons(ARPOP_REQUEST);
155
Paul Stewarta794cd62015-06-16 13:13:10 -0700156 *packet = ByteString(reinterpret_cast<const unsigned char*>(&header),
Paul Stewart91a5aac2012-07-20 11:55:40 -0700157 sizeof(header));
158
159 packet->Append(local_mac_address_);
160 packet->Append(local_ip_address_.address());
161 packet->Append(remote_mac_address_);
162 packet->Append(remote_ip_address_.address());
163
Paul Stewartac1328e2012-07-20 11:55:40 -0700164 if (packet->GetLength() < kMinPayloadSize) {
165 packet->Append(ByteString(kMinPayloadSize - packet->GetLength()));
166 }
167
Paul Stewart91a5aac2012-07-20 11:55:40 -0700168 return true;
169}
170
Paul Stewart417e5f02014-10-09 08:52:35 -0700171bool ArpPacket::IsReply() const {
172 return operation_ == ARPOP_REPLY;
173}
174
Paul Stewart91a5aac2012-07-20 11:55:40 -0700175} // namespace shill