1 // Copyright 2015 The Weave Authors. All rights reserved. 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 "examples/provider/wifi_manager.h" 6 7 #include <arpa/inet.h> 8 #include <dirent.h> 9 #include <linux/wireless.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 13 #include <fstream> 14 15 #include <base/bind.h> 16 #include <weave/provider/task_runner.h> 17 18 #include "examples/provider/event_network.h" 19 #include "examples/provider/ssl_stream.h" 20 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); 36 execvp(path.c_str(), const_cast<char**>(args_vector.data())); 37 NOTREACHED(); 38 return 0; 39 } 40 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 66 } // namespace 67 68 WifiImpl::WifiImpl(provider::TaskRunner* task_runner, EventNetworkImpl* network) 69 : task_runner_{task_runner}, network_{network}, iface_{FindWirelessInterface()} { 70 CHECK(!iface_.empty()) << "WiFi interface not found"; 71 CHECK_EQ(0u, getuid()) 72 << "\nWiFi manager expects root access to control WiFi capabilities"; 73 StopAccessPoint(); 74 } 75 WifiImpl::~WifiImpl() { 76 StopAccessPoint(); 77 } 78 79 void WifiImpl::TryToConnect(const std::string& ssid, 80 const std::string& passphrase, 81 int pid, 82 base::Time until, 83 const DoneCallback& callback) { 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 = {}; 91 strncpy(wreq.ifr_name, iface_.c_str(), sizeof(wreq.ifr_name)); 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 99 if (ssid == essid) 100 return task_runner_->PostDelayedTask(FROM_HERE, 101 base::Bind(callback, nullptr), {}); 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 111 if (base::Time::Now() >= until) { 112 ErrorPtr error; 113 Error::AddTo(&error, FROM_HERE, "timeout", 114 "Timeout connecting to WiFI network."); 115 task_runner_->PostDelayedTask( 116 FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); 117 return; 118 } 119 120 task_runner_->PostDelayedTask( 121 FROM_HERE, 122 base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid, 123 passphrase, pid, until, callback), 124 base::TimeDelta::FromSeconds(1)); 125 } 126 127 void WifiImpl::Connect(const std::string& ssid, 128 const std::string& passphrase, 129 const DoneCallback& callback) { 130 network_->SetSimulateOffline(false); 131 CHECK(!hostapd_started_); 132 if (hostapd_started_) { 133 ErrorPtr error; 134 Error::AddTo(&error, FROM_HERE, "busy", "Running Access Point."); 135 task_runner_->PostDelayedTask( 136 FROM_HERE, base::Bind(callback, base::Passed(&error)), {}); 137 return; 138 } 139 140 TryToConnect(ssid, passphrase, 0, 141 base::Time::Now() + base::TimeDelta::FromMinutes(1), callback); 142 } 143 144 void WifiImpl::StartAccessPoint(const std::string& ssid) { 145 if (hostapd_started_) 146 return; 147 148 // Release wifi interface. 149 CHECK_EQ(0, ForkCmdAndWait("nmcli", {"nm", "wifi", "off"})); 150 CHECK_EQ(0, ForkCmdAndWait("rfkill", {"unblock", "wlan"})); 151 sleep(1); 152 153 std::string hostapd_conf = "/tmp/weave_hostapd.conf"; 154 { 155 std::ofstream ofs(hostapd_conf); 156 ofs << "interface=" << iface_ << std::endl; 157 ofs << "channel=1" << std::endl; 158 ofs << "ssid=" << ssid << std::endl; 159 } 160 161 CHECK_EQ(0, ForkCmdAndWait("hostapd", {"-B", "-K", hostapd_conf})); 162 hostapd_started_ = true; 163 164 for (size_t i = 0; i < 10; ++i) { 165 if (0 == ForkCmdAndWait("ifconfig", {iface_, "192.168.76.1/24"})) 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; 177 ofs << "interface=" << iface_ << std::endl; 178 ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl; 179 } 180 181 CHECK_EQ(0, ForkCmdAndWait("dnsmasq", {"--conf-file=" + dnsmasq_conf})); 182 } 183 184 void WifiImpl::StopAccessPoint() { 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"})); 188 hostapd_started_ = false; 189 } 190 191 bool WifiImpl::HasWifiCapability() { 192 return !FindWirelessInterface().empty(); 193 } 194 195 } // namespace examples 196 } // namespace weave 197