xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/qbone/bonnet/icmp_reachable.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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