blob: 27bbfd644f6fd33b556b43a04bf17eba5ee78935 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Vitaly Buka17b0a8a2015-08-31 19:12:35 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Johan Euphrosine3523fdd2015-10-14 20:46:05 -07005#include "examples/provider/wifi_manager.h"
Vitaly Buka17b0a8a2015-08-31 19:12:35 -07006
7#include <arpa/inet.h>
Johan Euphrosineec47eb02015-12-11 09:17:44 +00008#include <dirent.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -07009#include <linux/wireless.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070010#include <sys/ioctl.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070011#include <sys/wait.h>
12
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070013#include <fstream>
14
15#include <base/bind.h>
Vitaly Buka1e363672015-09-25 14:01:16 -070016#include <weave/provider/task_runner.h>
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070017
Vitaly Buka3d8feaf2015-10-28 13:10:53 -070018#include "examples/provider/event_network.h"
Johan Euphrosine3523fdd2015-10-14 20:46:05 -070019#include "examples/provider/ssl_stream.h"
Vitaly Bukacd850602015-09-21 17:23:57 -070020
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070021namespace weave {
22namespace examples {
23
24namespace {
25
26int 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 Buka17b0a8a2015-08-31 19:12:35 -070036 execvp(path.c_str(), const_cast<char**>(args_vector.data()));
37 NOTREACHED();
Vitaly Buka8cca9b12015-09-29 14:49:44 -070038 return 0;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070039}
40
Johan Euphrosineec47eb02015-12-11 09:17:44 +000041int 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
Vitaly Buka6da60ad2016-02-23 12:07:07 -080049struct DirCloser {
50 void operator()(DIR* dir) { closedir(dir); }
51};
52
Johan Euphrosineec47eb02015-12-11 09:17:44 +000053std::string FindWirelessInterface() {
54 std::string sysfs_net{"/sys/class/net"};
Vitaly Buka6da60ad2016-02-23 12:07:07 -080055 std::unique_ptr<DIR, DirCloser> net_dir{opendir(sysfs_net.c_str())};
56 CHECK(net_dir);
Johan Euphrosineec47eb02015-12-11 09:17:44 +000057 dirent* iface;
Vitaly Buka6da60ad2016-02-23 12:07:07 -080058 while ((iface = readdir(net_dir.get()))) {
Johan Euphrosineec47eb02015-12-11 09:17:44 +000059 auto path = sysfs_net + "/" + iface->d_name + "/wireless";
Vitaly Buka6da60ad2016-02-23 12:07:07 -080060 std::unique_ptr<DIR, DirCloser> wireless_dir{opendir(path.c_str())};
61 if (wireless_dir)
Johan Euphrosineec47eb02015-12-11 09:17:44 +000062 return iface->d_name;
Johan Euphrosineec47eb02015-12-11 09:17:44 +000063 }
Johan Euphrosineec47eb02015-12-11 09:17:44 +000064 return "";
65}
66
Vitaly Bukaa07bbc72016-02-26 20:14:46 -080067std::string GetSsid(const std::string& interface) {
68 int sockf_d = socket(AF_INET, SOCK_DGRAM, 0);
69 CHECK_GE(sockf_d, 0) << strerror(errno);
70 iwreq wreq = {};
71 CHECK_LE(interface.size(), sizeof(wreq.ifr_name));
72 strncpy(wreq.ifr_name, interface.c_str(), sizeof(wreq.ifr_name));
73 std::string essid(' ', IW_ESSID_MAX_SIZE + 1);
74 wreq.u.essid.pointer = &essid[0];
75 wreq.u.essid.length = essid.size();
Vitaly Bukab741d642016-03-01 12:53:46 -080076 if (ioctl(sockf_d, SIOCGIWESSID, &wreq) >= 0)
77 essid.resize(wreq.u.essid.length);
78 else
79 essid.clear();
Vitaly Bukaa07bbc72016-02-26 20:14:46 -080080 close(sockf_d);
81 return essid;
82}
83
84bool CheckFreq(const std::string& interface, double start, double end) {
85 int sockf_d = socket(AF_INET, SOCK_DGRAM, 0);
86 CHECK_GE(sockf_d, 0) << strerror(errno);
87 iwreq wreq = {};
88 CHECK_LE(interface.size(), sizeof(wreq.ifr_name));
89 strncpy(wreq.ifr_name, interface.c_str(), sizeof(wreq.ifr_name));
90
91 iw_range range = {};
92 wreq.u.data.pointer = &range;
93 wreq.u.data.length = sizeof(range);
94
Vitaly Bukaa07bbc72016-02-26 20:14:46 -080095 bool result = false;
Vitaly Bukab741d642016-03-01 12:53:46 -080096 if (ioctl(sockf_d, SIOCGIWRANGE, &wreq) >= 0) {
97 for (size_t i = 0; !result && i < range.num_frequency; ++i) {
98 double freq = range.freq[i].m * std::pow(10., range.freq[i].e);
99 if (start <= freq && freq <= end)
100 result = true;
101 }
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800102 }
103
104 close(sockf_d);
105 return result;
106}
107
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700108} // namespace
109
Vitaly Buka3d8feaf2015-10-28 13:10:53 -0700110WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network)
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000111 : task_runner_{task_runner}, network_{network}, iface_{FindWirelessInterface()} {
112 CHECK(!iface_.empty()) << "WiFi interface not found";
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800113 CHECK(IsWifi24Supported() || IsWifi50Supported());
Johan Euphrosinefdf75152015-11-06 01:16:22 -0800114 CHECK_EQ(0u, getuid())
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000115 << "\nWiFi manager expects root access to control WiFi capabilities";
Vitaly Buka1fd619a2015-09-24 11:46:05 -0700116 StopAccessPoint();
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700117}
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700118WifiImpl::~WifiImpl() {
Vitaly Buka1fd619a2015-09-24 11:46:05 -0700119 StopAccessPoint();
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700120}
121
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700122void WifiImpl::TryToConnect(const std::string& ssid,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -0700123 const std::string& passphrase,
124 int pid,
125 base::Time until,
126 const DoneCallback& callback) {
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700127 if (pid) {
128 int status = 0;
129 if (pid == waitpid(pid, &status, WNOWAIT)) {
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800130 if (ssid == GetSsid(iface_))
Vitaly Buka74763422015-10-11 00:39:52 -0700131 return task_runner_->PostDelayedTask(FROM_HERE,
132 base::Bind(callback, nullptr), {});
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700133 pid = 0; // Try again.
134 }
135 }
136
137 if (pid == 0) {
138 pid = ForkCmd("nmcli",
139 {"dev", "wifi", "connect", ssid, "password", passphrase});
140 }
141
Vitaly Bukaaa301342015-09-21 16:08:33 -0700142 if (base::Time::Now() >= until) {
143 ErrorPtr error;
Vitaly Buka48a86692016-01-21 17:15:58 -0800144 Error::AddTo(&error, FROM_HERE, "timeout",
Vitaly Bukaaa301342015-09-21 16:08:33 -0700145 "Timeout connecting to WiFI network.");
146 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700147 FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700148 return;
Vitaly Bukaaa301342015-09-21 16:08:33 -0700149 }
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700150
151 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700152 FROM_HERE,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -0700153 base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid,
154 passphrase, pid, until, callback),
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700155 base::TimeDelta::FromSeconds(1));
156}
157
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700158void WifiImpl::Connect(const std::string& ssid,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -0700159 const std::string& passphrase,
160 const DoneCallback& callback) {
Vitaly Buka3d8feaf2015-10-28 13:10:53 -0700161 network_->SetSimulateOffline(false);
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800162 if (!hostapd_ssid_.empty()) {
Vitaly Bukaaa301342015-09-21 16:08:33 -0700163 ErrorPtr error;
Vitaly Buka48a86692016-01-21 17:15:58 -0800164 Error::AddTo(&error, FROM_HERE, "busy", "Running Access Point.");
Vitaly Bukaaa301342015-09-21 16:08:33 -0700165 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700166 FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
Vitaly Bukaaa301342015-09-21 16:08:33 -0700167 return;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700168 }
169
170 TryToConnect(ssid, passphrase, 0,
Vitaly Buka74763422015-10-11 00:39:52 -0700171 base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700172}
173
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700174void WifiImpl::StartAccessPoint(const std::string& ssid) {
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800175 CHECK(hostapd_ssid_.empty());
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700176
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000177 // Release wifi interface.
178 CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "off"}));
179 CHECK_EQ(0, ForkCmdAndWait("rfkill", {"unblock", "wlan"}));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700180 sleep(1);
181
182 std::string hostapd_conf = "/tmp/weave_hostapd.conf";
183 {
184 std::ofstream ofs(hostapd_conf);
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000185 ofs << "interface=" << iface_ << std::endl;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700186 ofs << "channel=1" << std::endl;
187 ofs << "ssid=" << ssid << std::endl;
188 }
189
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000190 CHECK_EQ(0, ForkCmdAndWait("hostapd", {"-B", "-K", hostapd_conf}));
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800191 hostapd_ssid_ = ssid;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700192
193 for (size_t i = 0; i < 10; ++i) {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000194 if (0 == ForkCmdAndWait("ifconfig", {iface_, "192.168.76.1/24"}))
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700195 break;
196 sleep(1);
197 }
198
199 std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf";
200 {
201 std::ofstream ofs(dnsmasq_conf.c_str());
202 ofs << "port=0" << std::endl;
203 ofs << "bind-interfaces" << std::endl;
204 ofs << "log-dhcp" << std::endl;
205 ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl;
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000206 ofs << "interface=" << iface_ << std::endl;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700207 ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl;
208 }
209
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000210 CHECK_EQ(0, ForkCmdAndWait("dnsmasq", {"--conf-file=" + dnsmasq_conf}));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700211}
212
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700213void WifiImpl::StopAccessPoint() {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000214 base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "dnsmasq.*/tmp/weave"}));
215 base::IgnoreResult(ForkCmdAndWait("pkill", {"-f", "hostapd.*/tmp/weave"}));
216 CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "on"}));
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800217 hostapd_ssid_.clear();
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700218}
219
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700220bool WifiImpl::HasWifiCapability() {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000221 return !FindWirelessInterface().empty();
Vitaly Bukae22a73f2015-09-21 17:39:32 -0700222}
223
Vitaly Bukaa07bbc72016-02-26 20:14:46 -0800224bool WifiImpl::IsWifi24Supported() const {
225 return CheckFreq(iface_, 2.4e9, 2.5e9);
226};
227
228bool WifiImpl::IsWifi50Supported() const {
229 return CheckFreq(iface_, 4.9e9, 5.9e9);
230};
231
232std::string WifiImpl::GetConnectedSsid() const {
233 return GetSsid(iface_);
234}
235
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700236} // namespace examples
237} // namespace weave