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