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