blob: 6b131ce97eb56b3e3c93ee584b4009fe189497d5 [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
12#include <base/logging.h>
13
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()
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
Paul Stewartac1328e2012-07-20 11:55:40 -0700162 if (packet->GetLength() < kMinPayloadSize) {
163 packet->Append(ByteString(kMinPayloadSize - packet->GetLength()));
164 }
165
Paul Stewart91a5aac2012-07-20 11:55:40 -0700166 return true;
167}
168
169} // namespace shill