blob: f9462405b2756a6ca028c9a923b9663cdca92ec2 [file] [log] [blame]
Paul Stewartac1328e2012-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_client.h"
6
7#include <linux/if_packet.h>
8#include <net/ethernet.h>
9#include <net/if_arp.h>
10#include <netinet/in.h>
11#include <string.h>
12
13#include <base/logging.h>
14
15#include "shill/arp_packet.h"
16#include "shill/byte_string.h"
17#include "shill/sockets.h"
18
19namespace shill {
20
21// ARP opcode is the last uint16 in the ARP header.
22const size_t ArpClient::kArpOpOffset = sizeof(arphdr) - sizeof(uint16);
23
24// The largest packet we expect is one with IPv6 addresses in it.
25const size_t ArpClient::kMaxArpPacketLength =
26 sizeof(arphdr) + sizeof(in6_addr) * 2 + ETH_ALEN * 2;
27
28ArpClient::ArpClient(int interface_index)
29 : interface_index_(interface_index),
30 sockets_(new Sockets()),
31 socket_(-1) {}
32
33ArpClient::~ArpClient() {}
34
35bool ArpClient::Start() {
36 if (!CreateSocket()) {
37 LOG(ERROR) << "Could not open ARP socket.";
38 Stop();
39 return false;
40 }
41 return true;
42}
43
44void ArpClient::Stop() {
45 socket_closer_.reset();
46}
47
48
49bool ArpClient::CreateSocket() {
50 int socket = sockets_->Socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_ARP));
51 if (socket == -1) {
52 PLOG(ERROR) << "Could not create ARP socket";
53 return false;
54 }
55 socket_ = socket;
56 socket_closer_.reset(new ScopedSocketCloser(sockets_.get(), socket_));
57
58 // Create a packet filter incoming ARP replies.
59 static const sock_filter arp_reply_filter[] = {
60 // If we a packet contains ARPOP_REPLY as the ARP opcode...
61 BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kArpOpOffset),
62 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REPLY, 0, 1),
63 // Return the the packet (up to largest expected packet size).
64 BPF_STMT(BPF_RET | BPF_K, kMaxArpPacketLength),
65 // Otherwise, drop it.
66 BPF_STMT(BPF_RET | BPF_K, 0),
67 };
68
69 sock_fprog pf;
70 pf.filter = const_cast<sock_filter *>(arp_reply_filter);
71 pf.len = arraysize(arp_reply_filter);
72 if (sockets_->AttachFilter(socket_, &pf) != 0) {
73 PLOG(ERROR) << "Could not attach packet filter";
74 return false;
75 }
76
77 if (sockets_->SetNonBlocking(socket_) != 0) {
78 PLOG(ERROR) << "Could not set socket to be non-blocking";
79 return false;
80 }
81
82 sockaddr_ll socket_address;
83 memset(&socket_address, 0, sizeof(socket_address));
84 socket_address.sll_family = AF_PACKET;
85 socket_address.sll_protocol = htons(ETHERTYPE_ARP);
86 socket_address.sll_ifindex = interface_index_;
87
88 if (sockets_->Bind(socket_,
89 reinterpret_cast<struct sockaddr *>(&socket_address),
90 sizeof(socket_address)) != 0) {
91 PLOG(ERROR) << "Could not bind socket to interface";
92 return false;
93 }
94
95 return true;
96}
97
98bool ArpClient::ReceiveReply(ArpPacket *packet, ByteString *sender) const {
99 ByteString payload(kMaxArpPacketLength);
100 sockaddr_ll socket_address;
101 memset(&socket_address, 0, sizeof(socket_address));
102 socklen_t socklen = sizeof(socket_address);
103 int result = sockets_->RecvFrom(
104 socket_,
105 payload.GetData(),
106 payload.GetLength(),
107 0,
108 reinterpret_cast<struct sockaddr *>(&socket_address),
109 &socklen);
110 if (result < 0) {
111 PLOG(ERROR) << "Socket recvfrom failed";
112 return false;
113 }
114
115 payload.Resize(result);
116 if (!packet->ParseReply(payload)) {
117 LOG(ERROR) << "Failed to parse ARP reply.";
118 return false;
119 }
120
121 // The socket address returned may only be big enough to contain
122 // the hardware address of the sender.
123 CHECK(socklen >=
124 sizeof(socket_address) - sizeof(socket_address.sll_addr) + ETH_ALEN);
125 CHECK(socket_address.sll_halen == ETH_ALEN);
126 *sender = ByteString(
127 reinterpret_cast<const unsigned char *>(&socket_address.sll_addr),
128 socket_address.sll_halen);
129 return true;
130}
131
132bool ArpClient::TransmitRequest(const ArpPacket &packet) const {
133 ByteString payload;
134 if (!packet.FormatRequest(&payload)) {
135 return false;
136 }
137
138 sockaddr_ll socket_address;
139 memset(&socket_address, 0, sizeof(socket_address));
140 socket_address.sll_family = AF_PACKET;
141 socket_address.sll_protocol = htons(ETHERTYPE_ARP);
142 socket_address.sll_hatype = ARPHRD_ETHER;
143 socket_address.sll_halen = ETH_ALEN;
144 socket_address.sll_ifindex = interface_index_;
145
146 ByteString remote_address = packet.remote_mac_address();
147 CHECK(sizeof(socket_address.sll_addr) >= remote_address.GetLength());
148 if (remote_address.IsZero()) {
149 // If the destination MAC address is unspecified, send the packet
150 // to the broadcast (all-ones) address.
151 remote_address.BitwiseInvert();
152 }
153 memcpy(&socket_address.sll_addr, remote_address.GetConstData(),
154 remote_address.GetLength());
155
156 int result = sockets_->SendTo(
157 socket_,
158 payload.GetConstData(),
159 payload.GetLength(),
160 0,
161 reinterpret_cast<struct sockaddr *>(&socket_address),
162 sizeof(socket_address));
163 const int expected_result = static_cast<int>(payload.GetLength());
164 if (result != expected_result) {
165 if (result < 0) {
166 PLOG(ERROR) << "Socket sendto failed";
167 } else if (result < static_cast<int>(payload.GetLength())) {
168 LOG(ERROR) << "Socket sendto returned "
169 << result
170 << " which is different from expected result "
171 << expected_result;
172 }
173 return false;
174 }
175
176 return true;
177}
178
179} // namespace shill