1 // Copyright (c) 2019 The Chromium 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 "quiche/quic/qbone/bonnet/icmp_reachable.h"
6
7 #include <netinet/ip6.h>
8
9 #include "absl/strings/string_view.h"
10 #include "quiche/quic/core/crypto/quic_random.h"
11 #include "quiche/quic/core/io/quic_event_loop.h"
12 #include "quiche/quic/platform/api/quic_logging.h"
13 #include "quiche/quic/platform/api/quic_mutex.h"
14 #include "quiche/quic/qbone/platform/icmp_packet.h"
15 #include "quiche/common/platform/api/quiche_logging.h"
16 #include "quiche/common/quiche_text_utils.h"
17
18 namespace quic {
19 namespace {
20
21 constexpr QuicSocketEventMask kEventMask =
22 kSocketEventReadable | kSocketEventWritable;
23 constexpr size_t kMtu = 1280;
24
25 constexpr size_t kIPv6AddrSize = sizeof(in6_addr);
26
27 } // namespace
28
29 const char kUnknownSource[] = "UNKNOWN";
30 const char kNoSource[] = "N/A";
31
IcmpReachable(QuicIpAddress source,QuicIpAddress destination,QuicTime::Delta timeout,KernelInterface * kernel,QuicEventLoop * event_loop,StatsInterface * stats)32 IcmpReachable::IcmpReachable(QuicIpAddress source, QuicIpAddress destination,
33 QuicTime::Delta timeout, KernelInterface* kernel,
34 QuicEventLoop* event_loop, StatsInterface* stats)
35 : timeout_(timeout),
36 event_loop_(event_loop),
37 clock_(event_loop->GetClock()),
38 alarm_factory_(event_loop->CreateAlarmFactory()),
39 cb_(this),
40 alarm_(alarm_factory_->CreateAlarm(new AlarmCallback(this))),
41 kernel_(kernel),
42 stats_(stats),
43 send_fd_(0),
44 recv_fd_(0) {
45 src_.sin6_family = AF_INET6;
46 dst_.sin6_family = AF_INET6;
47
48 memcpy(&src_.sin6_addr, source.ToPackedString().data(), kIPv6AddrSize);
49 memcpy(&dst_.sin6_addr, destination.ToPackedString().data(), kIPv6AddrSize);
50 }
51
~IcmpReachable()52 IcmpReachable::~IcmpReachable() {
53 if (send_fd_ > 0) {
54 kernel_->close(send_fd_);
55 }
56 if (recv_fd_ > 0) {
57 bool success = event_loop_->UnregisterSocket(recv_fd_);
58 QUICHE_DCHECK(success);
59
60 kernel_->close(recv_fd_);
61 }
62 }
63
Init()64 bool IcmpReachable::Init() {
65 send_fd_ = kernel_->socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
66 if (send_fd_ < 0) {
67 QUIC_LOG(ERROR) << "Unable to open socket: " << errno;
68 return false;
69 }
70
71 if (kernel_->bind(send_fd_, reinterpret_cast<struct sockaddr*>(&src_),
72 sizeof(sockaddr_in6)) < 0) {
73 QUIC_LOG(ERROR) << "Unable to bind socket: " << errno;
74 return false;
75 }
76
77 recv_fd_ =
78 kernel_->socket(PF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6);
79 if (recv_fd_ < 0) {
80 QUIC_LOG(ERROR) << "Unable to open socket: " << errno;
81 return false;
82 }
83
84 if (kernel_->bind(recv_fd_, reinterpret_cast<struct sockaddr*>(&src_),
85 sizeof(sockaddr_in6)) < 0) {
86 QUIC_LOG(ERROR) << "Unable to bind socket: " << errno;
87 return false;
88 }
89
90 icmp6_filter filter;
91 ICMP6_FILTER_SETBLOCKALL(&filter);
92 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
93 if (kernel_->setsockopt(recv_fd_, SOL_ICMPV6, ICMP6_FILTER, &filter,
94 sizeof(filter)) < 0) {
95 QUIC_LOG(ERROR) << "Unable to set ICMP6 filter.";
96 return false;
97 }
98
99 if (!event_loop_->RegisterSocket(recv_fd_, kEventMask, &cb_)) {
100 QUIC_LOG(ERROR) << "Unable to register recv ICMP socket";
101 return false;
102 }
103 alarm_->Set(clock_->Now());
104
105 QuicWriterMutexLock mu(&header_lock_);
106 icmp_header_.icmp6_type = ICMP6_ECHO_REQUEST;
107 icmp_header_.icmp6_code = 0;
108
109 QuicRandom::GetInstance()->RandBytes(&icmp_header_.icmp6_id,
110 sizeof(uint16_t));
111
112 return true;
113 }
114
OnEvent(int fd)115 bool IcmpReachable::OnEvent(int fd) {
116 char buffer[kMtu];
117
118 sockaddr_in6 source_addr{};
119 socklen_t source_addr_len = sizeof(source_addr);
120
121 ssize_t size = kernel_->recvfrom(fd, &buffer, kMtu, 0,
122 reinterpret_cast<sockaddr*>(&source_addr),
123 &source_addr_len);
124
125 if (size < 0) {
126 if (errno != EAGAIN && errno != EWOULDBLOCK) {
127 stats_->OnReadError(errno);
128 }
129 return false;
130 }
131
132 QUIC_VLOG(2) << quiche::QuicheTextUtils::HexDump(
133 absl::string_view(buffer, size));
134
135 auto* header = reinterpret_cast<const icmp6_hdr*>(&buffer);
136 QuicWriterMutexLock mu(&header_lock_);
137 if (header->icmp6_data32[0] != icmp_header_.icmp6_data32[0]) {
138 QUIC_VLOG(2) << "Unexpected response. id: " << header->icmp6_id
139 << " seq: " << header->icmp6_seq
140 << " Expected id: " << icmp_header_.icmp6_id
141 << " seq: " << icmp_header_.icmp6_seq;
142 return true;
143 }
144 end_ = clock_->Now();
145 QUIC_VLOG(1) << "Received ping response in " << (end_ - start_);
146
147 std::string source;
148 QuicIpAddress source_ip;
149 if (!source_ip.FromPackedString(
150 reinterpret_cast<char*>(&source_addr.sin6_addr), sizeof(in6_addr))) {
151 QUIC_LOG(WARNING) << "Unable to parse source address.";
152 source = kUnknownSource;
153 } else {
154 source = source_ip.ToString();
155 }
156 stats_->OnEvent({Status::REACHABLE, end_ - start_, source});
157 return true;
158 }
159
OnAlarm()160 void IcmpReachable::OnAlarm() {
161 QuicWriterMutexLock mu(&header_lock_);
162
163 if (end_ < start_) {
164 QUIC_VLOG(1) << "Timed out on sequence: " << icmp_header_.icmp6_seq;
165 stats_->OnEvent({Status::UNREACHABLE, QuicTime::Delta::Zero(), kNoSource});
166 }
167
168 icmp_header_.icmp6_seq++;
169 CreateIcmpPacket(src_.sin6_addr, dst_.sin6_addr, icmp_header_, "",
170 [this](absl::string_view packet) {
171 QUIC_VLOG(2) << quiche::QuicheTextUtils::HexDump(packet);
172
173 ssize_t size = kernel_->sendto(
174 send_fd_, packet.data(), packet.size(), 0,
175 reinterpret_cast<struct sockaddr*>(&dst_),
176 sizeof(sockaddr_in6));
177
178 if (size < packet.size()) {
179 stats_->OnWriteError(errno);
180 }
181 start_ = clock_->Now();
182 });
183
184 alarm_->Set(clock_->ApproximateNow() + timeout_);
185 }
186
StatusName(IcmpReachable::Status status)187 absl::string_view IcmpReachable::StatusName(IcmpReachable::Status status) {
188 switch (status) {
189 case REACHABLE:
190 return "REACHABLE";
191 case UNREACHABLE:
192 return "UNREACHABLE";
193 default:
194 return "UNKNOWN";
195 }
196 }
197
OnSocketEvent(QuicEventLoop * event_loop,SocketFd fd,QuicSocketEventMask events)198 void IcmpReachable::EpollCallback::OnSocketEvent(QuicEventLoop* event_loop,
199 SocketFd fd,
200 QuicSocketEventMask events) {
201 bool can_read_more = reachable_->OnEvent(fd);
202 if (can_read_more) {
203 bool success =
204 event_loop->ArtificiallyNotifyEvent(fd, kSocketEventReadable);
205 QUICHE_DCHECK(success);
206 }
207 }
208
209 } // namespace quic
210