blob: 7597e47f30433b9686dc746d34ec741d0dfb2f51 [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
49std::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 Buka17b0a8a2015-08-31 19:12:35 -070066} // namespace
67
Vitaly Buka3d8feaf2015-10-28 13:10:53 -070068WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network)
Johan Euphrosineec47eb02015-12-11 09:17:44 +000069 : task_runner_{task_runner}, network_{network}, iface_{FindWirelessInterface()} {
70 CHECK(!iface_.empty()) << "WiFi interface not found";
Johan Euphrosinefdf75152015-11-06 01:16:22 -080071 CHECK_EQ(0u, getuid())
Johan Euphrosineec47eb02015-12-11 09:17:44 +000072 << "\nWiFi manager expects root access to control WiFi capabilities";
Vitaly Buka1fd619a2015-09-24 11:46:05 -070073 StopAccessPoint();
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070074}
Johan Euphrosine09fcef62015-10-15 00:14:27 -070075WifiImpl::~WifiImpl() {
Vitaly Buka1fd619a2015-09-24 11:46:05 -070076 StopAccessPoint();
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070077}
78
Johan Euphrosine09fcef62015-10-15 00:14:27 -070079void WifiImpl::TryToConnect(const std::string& ssid,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -070080 const std::string& passphrase,
81 int pid,
82 base::Time until,
83 const DoneCallback& callback) {
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070084 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 Euphrosineec47eb02015-12-11 09:17:44 +000091 strncpy(wreq.ifr_name, iface_.c_str(), sizeof(wreq.ifr_name));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -070092 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 Buka4ebd6412015-09-21 15:02:22 -070099 if (ssid == essid)
Vitaly Buka74763422015-10-11 00:39:52 -0700100 return task_runner_->PostDelayedTask(FROM_HERE,
101 base::Bind(callback, nullptr), {});
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700102 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 Bukaaa301342015-09-21 16:08:33 -0700111 if (base::Time::Now() >= until) {
112 ErrorPtr error;
Vitaly Buka48a86692016-01-21 17:15:58 -0800113 Error::AddTo(&error, FROM_HERE, "timeout",
Vitaly Bukaaa301342015-09-21 16:08:33 -0700114 "Timeout connecting to WiFI network.");
115 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700116 FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700117 return;
Vitaly Bukaaa301342015-09-21 16:08:33 -0700118 }
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700119
120 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700121 FROM_HERE,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -0700122 base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid,
123 passphrase, pid, until, callback),
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700124 base::TimeDelta::FromSeconds(1));
125}
126
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700127void WifiImpl::Connect(const std::string& ssid,
Johan Euphrosine1ca3c222015-10-15 20:43:42 -0700128 const std::string& passphrase,
129 const DoneCallback& callback) {
Vitaly Buka3d8feaf2015-10-28 13:10:53 -0700130 network_->SetSimulateOffline(false);
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700131 CHECK(!hostapd_started_);
132 if (hostapd_started_) {
Vitaly Bukaaa301342015-09-21 16:08:33 -0700133 ErrorPtr error;
Vitaly Buka48a86692016-01-21 17:15:58 -0800134 Error::AddTo(&error, FROM_HERE, "busy", "Running Access Point.");
Vitaly Bukaaa301342015-09-21 16:08:33 -0700135 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700136 FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
Vitaly Bukaaa301342015-09-21 16:08:33 -0700137 return;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700138 }
139
140 TryToConnect(ssid, passphrase, 0,
Vitaly Buka74763422015-10-11 00:39:52 -0700141 base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700142}
143
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700144void WifiImpl::StartAccessPoint(const std::string& ssid) {
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700145 if (hostapd_started_)
146 return;
147
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000148 // Release wifi interface.
149 CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "off"}));
150 CHECK_EQ(0, ForkCmdAndWait("rfkill", {"unblock", "wlan"}));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700151 sleep(1);
152
153 std::string hostapd_conf = "/tmp/weave_hostapd.conf";
154 {
155 std::ofstream ofs(hostapd_conf);
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000156 ofs << "interface=" << iface_ << std::endl;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700157 ofs << "channel=1" << std::endl;
158 ofs << "ssid=" << ssid << std::endl;
159 }
160
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000161 CHECK_EQ(0, ForkCmdAndWait("hostapd", {"-B", "-K", hostapd_conf}));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700162 hostapd_started_ = true;
163
164 for (size_t i = 0; i < 10; ++i) {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000165 if (0 == ForkCmdAndWait("ifconfig", {iface_, "192.168.76.1/24"}))
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700166 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 Euphrosineec47eb02015-12-11 09:17:44 +0000177 ofs << "interface=" << iface_ << std::endl;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700178 ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl;
179 }
180
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000181 CHECK_EQ(0, ForkCmdAndWait("dnsmasq", {"--conf-file=" + dnsmasq_conf}));
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700182}
183
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700184void WifiImpl::StopAccessPoint() {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000185 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 Buka17b0a8a2015-08-31 19:12:35 -0700188 hostapd_started_ = false;
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700189}
190
Johan Euphrosine09fcef62015-10-15 00:14:27 -0700191bool WifiImpl::HasWifiCapability() {
Johan Euphrosineec47eb02015-12-11 09:17:44 +0000192 return !FindWirelessInterface().empty();
Vitaly Bukae22a73f2015-09-21 17:39:32 -0700193}
194
Vitaly Buka17b0a8a2015-08-31 19:12:35 -0700195} // namespace examples
196} // namespace weave