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