Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Johan Euphrosine | 3523fdd | 2015-10-14 20:46:05 -0700 | [diff] [blame] | 5 | #include "examples/provider/wifi_manager.h" |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 6 | |
| 7 | #include <arpa/inet.h> |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 8 | #include <dirent.h> |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 9 | #include <linux/wireless.h> |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 10 | #include <sys/ioctl.h> |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 11 | #include <sys/wait.h> |
| 12 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 13 | #include <fstream> |
| 14 | |
| 15 | #include <base/bind.h> |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 16 | #include <weave/provider/task_runner.h> |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 17 | |
Vitaly Buka | 3d8feaf | 2015-10-28 13:10:53 -0700 | [diff] [blame] | 18 | #include "examples/provider/event_network.h" |
Johan Euphrosine | 3523fdd | 2015-10-14 20:46:05 -0700 | [diff] [blame] | 19 | #include "examples/provider/ssl_stream.h" |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 20 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 21 | namespace weave { |
| 22 | namespace examples { |
| 23 | |
| 24 | namespace { |
| 25 | |
| 26 | int ForkCmd(const std::string& path, const std::vector<std::string>& args) { |
| 27 | int pid = fork(); |
| 28 | if (pid != 0) |
| 29 | return pid; |
| 30 | |
| 31 | std::vector<const char*> args_vector; |
| 32 | args_vector.push_back(path.c_str()); |
| 33 | for (auto& i : args) |
| 34 | args_vector.push_back(i.c_str()); |
| 35 | args_vector.push_back(nullptr); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 36 | execvp(path.c_str(), const_cast<char**>(args_vector.data())); |
| 37 | NOTREACHED(); |
Vitaly Buka | 8cca9b1 | 2015-09-29 14:49:44 -0700 | [diff] [blame] | 38 | return 0; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 39 | } |
| 40 | |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 41 | int ForkCmdAndWait(const std::string& path, |
| 42 | const std::vector<std::string>& args) { |
| 43 | int pid = ForkCmd(path, args); |
| 44 | int status = 0; |
| 45 | CHECK_EQ(pid, waitpid(pid, &status, 0)); |
| 46 | return status; |
| 47 | } |
| 48 | |
| 49 | std::string FindWirelessInterface() { |
| 50 | std::string sysfs_net{"/sys/class/net"}; |
| 51 | DIR* net_dir = opendir(sysfs_net.c_str()); |
| 52 | dirent* iface; |
| 53 | while ((iface = readdir(net_dir))) { |
| 54 | auto path = sysfs_net + "/" + iface->d_name + "/wireless"; |
| 55 | DIR* wireless_dir = opendir(path.c_str()); |
| 56 | if (wireless_dir != nullptr) { |
| 57 | closedir(net_dir); |
| 58 | closedir(wireless_dir); |
| 59 | return iface->d_name; |
| 60 | } |
| 61 | } |
| 62 | closedir(net_dir); |
| 63 | return ""; |
| 64 | } |
| 65 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 66 | } // namespace |
| 67 | |
Vitaly Buka | 3d8feaf | 2015-10-28 13:10:53 -0700 | [diff] [blame] | 68 | WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network) |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 69 | : task_runner_{task_runner}, network_{network}, iface_{FindWirelessInterface()} { |
| 70 | CHECK(!iface_.empty()) << "WiFi interface not found"; |
Johan Euphrosine | fdf7515 | 2015-11-06 01:16:22 -0800 | [diff] [blame] | 71 | CHECK_EQ(0u, getuid()) |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 72 | << "\nWiFi manager expects root access to control WiFi capabilities"; |
Vitaly Buka | 1fd619a | 2015-09-24 11:46:05 -0700 | [diff] [blame] | 73 | StopAccessPoint(); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 74 | } |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 75 | WifiImpl::~WifiImpl() { |
Vitaly Buka | 1fd619a | 2015-09-24 11:46:05 -0700 | [diff] [blame] | 76 | StopAccessPoint(); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 77 | } |
| 78 | |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 79 | void WifiImpl::TryToConnect(const std::string& ssid, |
Johan Euphrosine | 1ca3c22 | 2015-10-15 20:43:42 -0700 | [diff] [blame] | 80 | const std::string& passphrase, |
| 81 | int pid, |
| 82 | base::Time until, |
| 83 | const DoneCallback& callback) { |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 84 | if (pid) { |
| 85 | int status = 0; |
| 86 | if (pid == waitpid(pid, &status, WNOWAIT)) { |
| 87 | int sockf_d = socket(AF_INET, SOCK_DGRAM, 0); |
| 88 | CHECK_GE(sockf_d, 0) << strerror(errno); |
| 89 | |
| 90 | iwreq wreq = {}; |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 91 | strncpy(wreq.ifr_name, iface_.c_str(), sizeof(wreq.ifr_name)); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 92 | std::string essid(' ', IW_ESSID_MAX_SIZE + 1); |
| 93 | wreq.u.essid.pointer = &essid[0]; |
| 94 | wreq.u.essid.length = essid.size(); |
| 95 | CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno); |
| 96 | essid.resize(wreq.u.essid.length); |
| 97 | close(sockf_d); |
| 98 | |
Vitaly Buka | 4ebd641 | 2015-09-21 15:02:22 -0700 | [diff] [blame] | 99 | if (ssid == essid) |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 100 | return task_runner_->PostDelayedTask(FROM_HERE, |
| 101 | base::Bind(callback, nullptr), {}); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 102 | pid = 0; // Try again. |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | if (pid == 0) { |
| 107 | pid = ForkCmd("nmcli", |
| 108 | {"dev", "wifi", "connect", ssid, "password", passphrase}); |
| 109 | } |
| 110 | |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 111 | if (base::Time::Now() >= until) { |
| 112 | ErrorPtr error; |
Vitaly Buka | 48a8669 | 2016-01-21 17:15:58 -0800 | [diff] [blame] | 113 | Error::AddTo(&error, FROM_HERE, "timeout", |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 114 | "Timeout connecting to WiFI network."); |
| 115 | task_runner_->PostDelayedTask( |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 116 | FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 117 | return; |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 118 | } |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 119 | |
| 120 | task_runner_->PostDelayedTask( |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 121 | FROM_HERE, |
Johan Euphrosine | 1ca3c22 | 2015-10-15 20:43:42 -0700 | [diff] [blame] | 122 | base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid, |
| 123 | passphrase, pid, until, callback), |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 124 | base::TimeDelta::FromSeconds(1)); |
| 125 | } |
| 126 | |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 127 | void WifiImpl::Connect(const std::string& ssid, |
Johan Euphrosine | 1ca3c22 | 2015-10-15 20:43:42 -0700 | [diff] [blame] | 128 | const std::string& passphrase, |
| 129 | const DoneCallback& callback) { |
Vitaly Buka | 3d8feaf | 2015-10-28 13:10:53 -0700 | [diff] [blame] | 130 | network_->SetSimulateOffline(false); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 131 | CHECK(!hostapd_started_); |
| 132 | if (hostapd_started_) { |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 133 | ErrorPtr error; |
Vitaly Buka | 48a8669 | 2016-01-21 17:15:58 -0800 | [diff] [blame] | 134 | Error::AddTo(&error, FROM_HERE, "busy", "Running Access Point."); |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 135 | task_runner_->PostDelayedTask( |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 136 | FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); |
Vitaly Buka | aa30134 | 2015-09-21 16:08:33 -0700 | [diff] [blame] | 137 | return; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | TryToConnect(ssid, passphrase, 0, |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 141 | base::Time::Now() + base::TimeDelta::FromMinutes(1), callback); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 142 | } |
| 143 | |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 144 | void WifiImpl::StartAccessPoint(const std::string& ssid) { |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 145 | if (hostapd_started_) |
| 146 | return; |
| 147 | |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 148 | // Release wifi interface. |
| 149 | CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "off"})); |
| 150 | CHECK_EQ(0, ForkCmdAndWait("rfkill", {"unblock", "wlan"})); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 151 | sleep(1); |
| 152 | |
| 153 | std::string hostapd_conf = "/tmp/weave_hostapd.conf"; |
| 154 | { |
| 155 | std::ofstream ofs(hostapd_conf); |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 156 | ofs << "interface=" << iface_ << std::endl; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 157 | ofs << "channel=1" << std::endl; |
| 158 | ofs << "ssid=" << ssid << std::endl; |
| 159 | } |
| 160 | |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 161 | CHECK_EQ(0, ForkCmdAndWait("hostapd", {"-B", "-K", hostapd_conf})); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 162 | hostapd_started_ = true; |
| 163 | |
| 164 | for (size_t i = 0; i < 10; ++i) { |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 165 | if (0 == ForkCmdAndWait("ifconfig", {iface_, "192.168.76.1/24"})) |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 166 | break; |
| 167 | sleep(1); |
| 168 | } |
| 169 | |
| 170 | std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf"; |
| 171 | { |
| 172 | std::ofstream ofs(dnsmasq_conf.c_str()); |
| 173 | ofs << "port=0" << std::endl; |
| 174 | ofs << "bind-interfaces" << std::endl; |
| 175 | ofs << "log-dhcp" << std::endl; |
| 176 | ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl; |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 177 | ofs << "interface=" << iface_ << std::endl; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 178 | ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl; |
| 179 | } |
| 180 | |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 181 | CHECK_EQ(0, ForkCmdAndWait("dnsmasq", {"--conf-file=" + dnsmasq_conf})); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 182 | } |
| 183 | |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 184 | void WifiImpl::StopAccessPoint() { |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 185 | base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "dnsmasq.*/tmp/weave"})); |
| 186 | base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "hostapd.*/tmp/weave"})); |
| 187 | CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "on"})); |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 188 | hostapd_started_ = false; |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 189 | } |
| 190 | |
Johan Euphrosine | 09fcef6 | 2015-10-15 00:14:27 -0700 | [diff] [blame] | 191 | bool WifiImpl::HasWifiCapability() { |
Johan Euphrosine | ec47eb0 | 2015-12-11 09:17:44 +0000 | [diff] [blame] | 192 | return !FindWirelessInterface().empty(); |
Vitaly Buka | e22a73f | 2015-09-21 17:39:32 -0700 | [diff] [blame] | 193 | } |
| 194 | |
Vitaly Buka | 17b0a8a | 2015-08-31 19:12:35 -0700 | [diff] [blame] | 195 | } // namespace examples |
| 196 | } // namespace weave |