1 // Copyright 2018 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/network_change_notifier_fuchsia.h"
6
7 #include <fuchsia/net/interfaces/cpp/fidl.h>
8 #include <lib/sys/cpp/component_context.h>
9
10 #include <algorithm>
11 #include <optional>
12 #include <utility>
13 #include <vector>
14
15 #include "base/fuchsia/fuchsia_logging.h"
16 #include "base/fuchsia/process_context.h"
17 #include "base/logging.h"
18 #include "base/process/process.h"
19 #include "base/threading/thread_checker.h"
20 #include "base/types/expected.h"
21 #include "net/base/fuchsia/network_interface_cache.h"
22
23 namespace net {
24
NetworkChangeNotifierFuchsia(bool require_wlan)25 NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan)
26 : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(),
27 require_wlan,
28 /*system_dns_config_notifier=*/nullptr) {}
29
NetworkChangeNotifierFuchsia(fuchsia::net::interfaces::WatcherHandle watcher_handle,bool require_wlan,SystemDnsConfigChangeNotifier * system_dns_config_notifier)30 NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
31 fuchsia::net::interfaces::WatcherHandle watcher_handle,
32 bool require_wlan,
33 SystemDnsConfigChangeNotifier* system_dns_config_notifier)
34 : NetworkChangeNotifier(NetworkChangeCalculatorParams(),
35 system_dns_config_notifier),
36 cache_(require_wlan) {
37 DCHECK(watcher_handle);
38
39 std::vector<fuchsia::net::interfaces::Properties> interfaces;
40 auto handle_or_status = internal::ReadExistingNetworkInterfacesFromNewWatcher(
41 std::move(watcher_handle), interfaces);
42 if (!handle_or_status.has_value()) {
43 ZX_LOG(ERROR, handle_or_status.error()) << "ReadExistingNetworkInterfaces";
44 base::Process::TerminateCurrentProcessImmediately(1);
45 }
46
47 HandleCacheStatus(cache_.AddInterfaces(std::move(interfaces)));
48
49 watcher_.set_error_handler(base::LogFidlErrorAndExitProcess(
50 FROM_HERE, "fuchsia.net.interfaces.Watcher"));
51 zx_status_t bind_status = watcher_.Bind(std::move(handle_or_status.value()));
52 ZX_CHECK(bind_status == ZX_OK, bind_status) << "Bind()";
53 watcher_->Watch(
54 fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
55 }
56
~NetworkChangeNotifierFuchsia()57 NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() {
58 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
59 ClearGlobalPointer();
60 }
61
62 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType() const63 NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const {
64 return cache_.GetConnectionType();
65 }
66
67 const internal::NetworkInterfaceCache*
GetNetworkInterfaceCacheInternal() const68 NetworkChangeNotifierFuchsia::GetNetworkInterfaceCacheInternal() const {
69 return &cache_;
70 }
71
OnInterfacesEvent(fuchsia::net::interfaces::Event event)72 void NetworkChangeNotifierFuchsia::OnInterfacesEvent(
73 fuchsia::net::interfaces::Event event) {
74 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
75
76 // Immediately trigger the next watch, which will happen asynchronously. If
77 // event processing encounters an error it'll close the watcher channel which
78 // will cancel any pending callbacks.
79 watcher_->Watch(
80 fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
81
82 switch (event.Which()) {
83 case fuchsia::net::interfaces::Event::kAdded:
84 HandleCacheStatus(cache_.AddInterface(std::move(event.added())));
85 break;
86 case fuchsia::net::interfaces::Event::kRemoved:
87 HandleCacheStatus(cache_.RemoveInterface(event.removed()));
88 break;
89 case fuchsia::net::interfaces::Event::kChanged:
90 HandleCacheStatus(cache_.ChangeInterface(std::move(event.changed())));
91 break;
92 default:
93 LOG(ERROR) << "Unexpected event: " << event.Which();
94 watcher_.Unbind();
95 cache_.SetError();
96 break;
97 }
98 }
99
HandleCacheStatus(std::optional<internal::NetworkInterfaceCache::ChangeBits> change_bits)100 void NetworkChangeNotifierFuchsia::HandleCacheStatus(
101 std::optional<internal::NetworkInterfaceCache::ChangeBits> change_bits) {
102 if (!change_bits.has_value()) {
103 watcher_.Unbind();
104 return;
105 }
106
107 if (change_bits.value() &
108 internal::NetworkInterfaceCache::kIpAddressChanged) {
109 NotifyObserversOfIPAddressChange();
110 }
111 if (change_bits.value() &
112 internal::NetworkInterfaceCache::kConnectionTypeChanged) {
113 NotifyObserversOfConnectionTypeChange();
114 }
115 }
116
117 namespace internal {
118
ConnectInterfacesWatcher()119 fuchsia::net::interfaces::WatcherHandle ConnectInterfacesWatcher() {
120 fuchsia::net::interfaces::StateSyncPtr state;
121 zx_status_t status =
122 base::ComponentContextForProcess()->svc()->Connect(state.NewRequest());
123 ZX_CHECK(status == ZX_OK, status) << "Connect()";
124
125 // GetWatcher() is a feed-forward API, so failures will be observed via
126 // peer-closed events on the returned `watcher`.
127 fuchsia::net::interfaces::WatcherHandle watcher;
128 status = state->GetWatcher(/*options=*/{}, watcher.NewRequest());
129
130 return watcher;
131 }
132
133 base::expected<fuchsia::net::interfaces::WatcherHandle, zx_status_t>
ReadExistingNetworkInterfacesFromNewWatcher(fuchsia::net::interfaces::WatcherHandle watcher_handle,std::vector<fuchsia::net::interfaces::Properties> & interfaces)134 ReadExistingNetworkInterfacesFromNewWatcher(
135 fuchsia::net::interfaces::WatcherHandle watcher_handle,
136 std::vector<fuchsia::net::interfaces::Properties>& interfaces) {
137 DCHECK(watcher_handle);
138
139 fuchsia::net::interfaces::WatcherSyncPtr watcher = watcher_handle.BindSync();
140
141 // fuchsia.net.interfaces.Watcher implements a hanging-get pattern, accepting
142 // a single Watch() call and returning an event when something changes.
143 // When a Watcher is first created, it emits a series of events describing
144 // existing interfaces, terminated by an "idle" event, before entering the
145 // normal hanging-get flow.
146 while (true) {
147 fuchsia::net::interfaces::Event event;
148 if (auto watch_status = watcher->Watch(&event); watch_status != ZX_OK) {
149 ZX_LOG(ERROR, watch_status) << "Watch() failed";
150 return base::unexpected(watch_status);
151 }
152
153 switch (event.Which()) {
154 case fuchsia::net::interfaces::Event::Tag::kExisting:
155 interfaces.push_back(std::move(event.existing()));
156 break;
157 case fuchsia::net::interfaces::Event::Tag::kIdle:
158 // Idle means we've listed all the existing interfaces. We can stop
159 // fetching events.
160 return base::ok(watcher.Unbind());
161 default:
162 LOG(ERROR) << "Unexpected event " << event.Which();
163 return base::unexpected(ZX_ERR_BAD_STATE);
164 }
165 }
166 }
167
168 } // namespace internal
169 } // namespace net
170