xref: /aosp_15_r20/external/cronet/net/base/fuchsia/network_interface_cache.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 The Chromium Authors
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 "net/base/fuchsia/network_interface_cache.h"
6 
7 #include <fuchsia/net/interfaces/cpp/fidl.h>
8 
9 #include <optional>
10 #include <utility>
11 
12 #include "base/containers/flat_map.h"
13 #include "base/logging.h"
14 #include "base/sequence_checker.h"
15 #include "base/synchronization/lock.h"
16 #include "net/base/network_change_notifier.h"
17 #include "net/base/network_interfaces.h"
18 #include "net/base/network_interfaces_fuchsia.h"
19 
20 namespace net::internal {
21 namespace {
22 
23 // Returns a ConnectionType derived from the supplied InterfaceProperties:
24 // - CONNECTION_NONE if the interface is not publicly routable.
25 // - Otherwise, returns a type derived from the interface's device_class.
GetEffectiveConnectionType(const InterfaceProperties & properties,bool require_wlan)26 NetworkChangeNotifier::ConnectionType GetEffectiveConnectionType(
27     const InterfaceProperties& properties,
28     bool require_wlan) {
29   if (!properties.IsPubliclyRoutable()) {
30     return NetworkChangeNotifier::CONNECTION_NONE;
31   }
32 
33   NetworkChangeNotifier::ConnectionType connection_type =
34       ConvertConnectionType(properties.device_class());
35   if (require_wlan &&
36       connection_type != NetworkChangeNotifier::CONNECTION_WIFI) {
37     return NetworkChangeNotifier::CONNECTION_NONE;
38   }
39   return connection_type;
40 }
41 
CanReachExternalNetwork(const InterfaceProperties & interface,bool require_wlan)42 bool CanReachExternalNetwork(const InterfaceProperties& interface,
43                              bool require_wlan) {
44   return GetEffectiveConnectionType(interface, require_wlan) !=
45          NetworkChangeNotifier::CONNECTION_NONE;
46 }
47 
48 }  // namespace
49 
NetworkInterfaceCache(bool require_wlan)50 NetworkInterfaceCache::NetworkInterfaceCache(bool require_wlan)
51     : require_wlan_(require_wlan) {}
52 
53 NetworkInterfaceCache::~NetworkInterfaceCache() = default;
54 
55 std::optional<NetworkInterfaceCache::ChangeBits>
AddInterfaces(std::vector<fuchsia::net::interfaces::Properties> interfaces)56 NetworkInterfaceCache::AddInterfaces(
57     std::vector<fuchsia::net::interfaces::Properties> interfaces) {
58   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
59 
60   base::AutoLock auto_lock(lock_);
61 
62   ChangeBits combined_changes = kNoChange;
63   for (auto& interface : interfaces) {
64     auto change_bits = AddInterfaceWhileLocked(std::move(interface));
65     if (!change_bits.has_value()) {
66       return std::nullopt;
67     }
68     combined_changes |= change_bits.value();
69   }
70   return combined_changes;
71 }
72 
73 std::optional<NetworkInterfaceCache::ChangeBits>
AddInterface(fuchsia::net::interfaces::Properties properties)74 NetworkInterfaceCache::AddInterface(
75     fuchsia::net::interfaces::Properties properties) {
76   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
77 
78   base::AutoLock auto_lock(lock_);
79 
80   return AddInterfaceWhileLocked(std::move(properties));
81 }
82 
83 std::optional<NetworkInterfaceCache::ChangeBits>
AddInterfaceWhileLocked(fuchsia::net::interfaces::Properties properties)84 NetworkInterfaceCache::AddInterfaceWhileLocked(
85     fuchsia::net::interfaces::Properties properties)
86     EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
87   if (error_state_) {
88     return std::nullopt;
89   }
90 
91   auto interface = InterfaceProperties::VerifyAndCreate(std::move(properties));
92   if (!interface) {
93     LOG(ERROR) << "Incomplete interface properties.";
94     SetErrorWhileLocked();
95     return std::nullopt;
96   }
97 
98   if (interfaces_.find(interface->id()) != interfaces_.end()) {
99     LOG(ERROR) << "Unexpected duplicate interface ID " << interface->id();
100     SetErrorWhileLocked();
101     return std::nullopt;
102   }
103 
104   ChangeBits change_bits = kNoChange;
105   if (CanReachExternalNetwork(*interface, require_wlan_)) {
106     change_bits |= kIpAddressChanged;
107   }
108   interfaces_.emplace(interface->id(), std::move(*interface));
109   if (UpdateConnectionTypeWhileLocked()) {
110     change_bits |= kConnectionTypeChanged;
111   }
112   return change_bits;
113 }
114 
115 std::optional<NetworkInterfaceCache::ChangeBits>
ChangeInterface(fuchsia::net::interfaces::Properties properties)116 NetworkInterfaceCache::ChangeInterface(
117     fuchsia::net::interfaces::Properties properties) {
118   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
119 
120   base::AutoLock auto_lock(lock_);
121   if (error_state_) {
122     return std::nullopt;
123   }
124 
125   auto cache_entry = interfaces_.find(properties.id());
126   if (cache_entry == interfaces_.end()) {
127     LOG(ERROR) << "Unknown interface ID " << properties.id();
128     SetErrorWhileLocked();
129     return std::nullopt;
130   }
131 
132   const bool old_can_reach =
133       CanReachExternalNetwork(cache_entry->second, require_wlan_);
134   const bool has_addresses = properties.has_addresses();
135 
136   if (!cache_entry->second.Update(std::move(properties))) {
137     LOG(ERROR) << "Update failed";
138     SetErrorWhileLocked();
139     return std::nullopt;
140   }
141 
142   const bool new_can_reach =
143       CanReachExternalNetwork(cache_entry->second, require_wlan_);
144 
145   ChangeBits change_bits = kNoChange;
146   if (has_addresses || old_can_reach != new_can_reach) {
147     change_bits |= kIpAddressChanged;
148   }
149   if (UpdateConnectionTypeWhileLocked()) {
150     change_bits |= kConnectionTypeChanged;
151   }
152   return change_bits;
153 }
154 
155 std::optional<NetworkInterfaceCache::ChangeBits>
RemoveInterface(InterfaceProperties::InterfaceId interface_id)156 NetworkInterfaceCache::RemoveInterface(
157     InterfaceProperties::InterfaceId interface_id) {
158   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
159 
160   base::AutoLock auto_lock(lock_);
161   if (error_state_) {
162     return std::nullopt;
163   }
164 
165   auto cache_entry = interfaces_.find(interface_id);
166   if (cache_entry == interfaces_.end()) {
167     LOG(ERROR) << "Unknown interface ID " << interface_id;
168     SetErrorWhileLocked();
169     return std::nullopt;
170   }
171 
172   ChangeBits change_bits = kNoChange;
173   if (CanReachExternalNetwork(cache_entry->second, require_wlan_)) {
174     change_bits |= kIpAddressChanged;
175   }
176   interfaces_.erase(cache_entry);
177   if (UpdateConnectionTypeWhileLocked()) {
178     change_bits |= kConnectionTypeChanged;
179   }
180   return change_bits;
181 }
182 
GetOnlineInterfaces(NetworkInterfaceList * networks) const183 bool NetworkInterfaceCache::GetOnlineInterfaces(
184     NetworkInterfaceList* networks) const {
185   DCHECK(networks);
186 
187   base::AutoLock auto_lock(lock_);
188   if (error_state_) {
189     return false;
190   }
191 
192   for (const auto& [_, interface] : interfaces_) {
193     if (!interface.online()) {
194       continue;
195     }
196     if (interface.device_class().is_loopback()) {
197       continue;
198     }
199     interface.AppendNetworkInterfaces(networks);
200   }
201   return true;
202 }
203 
GetConnectionType() const204 NetworkChangeNotifier::ConnectionType NetworkInterfaceCache::GetConnectionType()
205     const {
206   base::AutoLock auto_lock(lock_);
207   if (error_state_) {
208     return NetworkChangeNotifier::CONNECTION_UNKNOWN;
209   }
210 
211   return connection_type_;
212 }
213 
SetError()214 void NetworkInterfaceCache::SetError() {
215   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
216 
217   base::AutoLock auto_lock(lock_);
218   SetErrorWhileLocked();
219 }
220 
UpdateConnectionTypeWhileLocked()221 bool NetworkInterfaceCache::UpdateConnectionTypeWhileLocked()
222     EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
223   NetworkChangeNotifier::ConnectionType connection_type =
224       NetworkChangeNotifier::ConnectionType::CONNECTION_NONE;
225   for (const auto& [_, interface] : interfaces_) {
226     connection_type = GetEffectiveConnectionType(interface, require_wlan_);
227     if (connection_type != NetworkChangeNotifier::CONNECTION_NONE) {
228       break;
229     }
230   }
231   if (connection_type != connection_type_) {
232     connection_type_ = connection_type;
233     return true;
234   }
235   return false;
236 }
237 
SetErrorWhileLocked()238 void NetworkInterfaceCache::SetErrorWhileLocked()
239     EXCLUSIVE_LOCKS_REQUIRED(lock_) VALID_CONTEXT_REQUIRED(sequence_checker_) {
240   error_state_ = true;
241   interfaces_.clear();
242   interfaces_.shrink_to_fit();
243 }
244 
245 }  // namespace net::internal
246