1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "wifi_relay.h" 18 19 #include "common/commands/wifi_relay/mac80211_hwsim_driver.h" 20 #include "common/commands/wifi_relay/nl_client.h" 21 22 #if defined(CUTTLEFISH_HOST) 23 #include "host/libs/config/cuttlefish_config.h" 24 #endif 25 26 #include <linux/netdevice.h> 27 #include <linux/nl80211.h> 28 #include <netlink/genl/ctrl.h> 29 #include <netlink/genl/genl.h> 30 31 #include <gflags/gflags.h> 32 #include <glog/logging.h> 33 34 #include <fstream> 35 36 #if !defined(CUTTLEFISH_HOST) 37 DEFINE_string( 38 iface_name, "wlan0", "Name of the wifi interface to be created."); 39 #endif 40 41 WifiRelay::WifiRelay( 42 const Mac80211HwSim::MacAddress &localMAC, 43 const Mac80211HwSim::MacAddress &remoteMAC) 44 : mMac80211HwSim(new Mac80211HwSim(localMAC)) { 45 init_check_ = mMac80211HwSim->initCheck(); 46 47 if (init_check_ < 0) { 48 return; 49 } 50 51 init_check_ = mMac80211HwSim->addRemote( 52 remoteMAC, 53 #if defined(CUTTLEFISH_HOST) 54 vsoc::wifi::WifiExchangeView::GetInstance(vsoc::GetDomain().c_str()) 55 #else 56 vsoc::wifi::WifiExchangeView::GetInstance() 57 #endif 58 ); 59 } 60 61 int WifiRelay::initCheck() const { 62 return init_check_; 63 } 64 65 void WifiRelay::run() { 66 for (;;) { 67 fd_set rs; 68 FD_ZERO(&rs); 69 70 FD_SET(mMac80211HwSim->socketFd(), &rs); 71 int maxFd = mMac80211HwSim->socketFd(); 72 73 int res = select(maxFd + 1, &rs, nullptr, nullptr, nullptr); 74 if (res <= 0) { 75 continue; 76 } 77 78 if (FD_ISSET(mMac80211HwSim->socketFd(), &rs)) { 79 mMac80211HwSim->handlePacket(); 80 } 81 } 82 } 83 84 int WifiRelay::mac80211Family() const { 85 return mMac80211HwSim->mac80211Family(); 86 } 87 88 int WifiRelay::nl80211Family() const { 89 return mMac80211HwSim->nl80211Family(); 90 } 91 92 int createRadio(cvd::NlClient *nl, int familyMAC80211, const char *phyName) { 93 cvd::Cmd msg; 94 genlmsg_put( 95 msg.Msg(), 96 NL_AUTO_PID, 97 NL_AUTO_SEQ, 98 familyMAC80211, 99 0, 100 NLM_F_REQUEST, 101 HWSIM_CMD_NEW_RADIO, 102 cvd::kWifiSimVersion); 103 104 nla_put_string(msg.Msg(), HWSIM_ATTR_RADIO_NAME, phyName); 105 nla_put_flag(msg.Msg(), HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE); 106 107 nl->Send(&msg); 108 109 // Responses() pauses until netlink responds to previously sent message. 110 for (auto *r : msg.Responses()) { 111 auto hdr = nlmsg_hdr(r); 112 if (hdr->nlmsg_type == NLMSG_ERROR) { 113 nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr)); 114 return err->error; 115 } 116 } 117 118 return -1; 119 } 120 121 int getPhyIndex(const std::string &phyName) { 122 std::ifstream file("/sys/class/ieee80211/" + phyName + "/index"); 123 124 int number; 125 file >> number; 126 127 return number; 128 } 129 130 int getInterfaceIndex(cvd::NlClient *nl, int familyNL80211, uint32_t phyIndex) { 131 cvd::Cmd msg; 132 genlmsg_put( 133 msg.Msg(), 134 NL_AUTO_PID, 135 NL_AUTO_SEQ, 136 familyNL80211, 137 0, 138 NLM_F_REQUEST | NLM_F_DUMP, 139 NL80211_CMD_GET_INTERFACE, 140 0); 141 142 nl->Send(&msg); 143 144 // Responses() pauses until netlink responds to previously sent message. 145 for (auto *r : msg.Responses()) { 146 auto hdr = nlmsg_hdr(r); 147 if (hdr->nlmsg_type == NLMSG_ERROR) { 148 nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr)); 149 return err->error; 150 } 151 152 // Last message in entire series. 153 if (hdr->nlmsg_type == NLMSG_DONE) { 154 break; 155 } 156 157 // !DONE && !ERROR => content. 158 // Decode attributes supplied by netlink. 159 // the genlmsg_parse puts each attribute in a respective slot in an array, 160 // so we have to preallocate enough space. 161 struct nlattr* attrs[NL80211_ATTR_MAX + 1]; 162 auto err = genlmsg_parse(hdr, 0, attrs, NL80211_ATTR_MAX, nullptr); 163 164 // Return error if response could not be parsed. This is actually quite 165 // serious. 166 if (err < 0) { 167 LOG(ERROR) << "Could not process netlink response: " << strerror(-err); 168 return err; 169 } 170 171 // Check if we have WIPHY attribute in response -- and if it's the relevant 172 // one. 173 auto wiphy = attrs[NL80211_ATTR_WIPHY]; 174 if (wiphy != nullptr && nla_get_u32(wiphy) == phyIndex) { 175 auto number = attrs[NL80211_ATTR_IFINDEX]; 176 177 if (number != nullptr) { 178 return nla_get_u32(number); 179 } 180 } 181 } 182 183 return -1; 184 } 185 186 int updateInterface( 187 cvd::NlClient *nlRoute, 188 int ifaceIndex, 189 const std::string &name, 190 const uint8_t *mac) { 191 cvd::Cmd msg; 192 193 ifinfomsg ifm{}; 194 ifm.ifi_index = ifaceIndex; 195 196 nlmsg_put( 197 msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, RTM_SETLINK, 0, NLM_F_REQUEST); 198 199 nlmsg_append(msg.Msg(), &ifm, sizeof(ifm), 0); 200 nla_put_string(msg.Msg(), IFLA_IFNAME, name.c_str()); 201 202 std::vector<uint8_t> macCopy(MAX_ADDR_LEN); 203 memcpy(&macCopy[0], mac, ETH_ALEN); 204 205 nla_put(msg.Msg(), IFLA_ADDRESS, MAX_ADDR_LEN, &macCopy[0]); 206 207 nlRoute->Send(&msg); 208 209 // Responses() pauses until netlink responds to previously sent message. 210 for (auto *r : msg.Responses()) { 211 auto hdr = nlmsg_hdr(r); 212 LOG(VERBOSE) << "got response of type " << hdr->nlmsg_type; 213 214 if (hdr->nlmsg_type == NLMSG_ERROR) { 215 nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr)); 216 217 if (err->error < 0) { 218 LOG(ERROR) << "updateInterface failed w/ " << err->error 219 << " (" << strerror(-err->error) << ")"; 220 } 221 222 return err->error; 223 } 224 } 225 226 LOG(VERBOSE) << "No more responses"; 227 228 return -1; 229 } 230 231 int main(int argc, char **argv) { 232 ::android::base::InitLogging(argv, android::base::StderrLogger); 233 gflags::ParseCommandLineFlags(&argc, &argv, true); 234 235 auto wifi_view = vsoc::wifi::WifiExchangeView::GetInstance( 236 #if defined(CUTTLEFISH_HOST) 237 vsoc::GetDomain().c_str() 238 #endif 239 ); 240 241 Mac80211HwSim::MacAddress guestMAC = wifi_view->GetGuestMACAddress(); 242 Mac80211HwSim::MacAddress hostMAC = wifi_view->GetHostMACAddress(); 243 244 #ifdef CUTTLEFISH_HOST 245 WifiRelay relay(hostMAC, guestMAC); 246 #else 247 WifiRelay relay(guestMAC, hostMAC); 248 #endif 249 int res = relay.initCheck(); 250 251 if (res < 0) { 252 LOG(ERROR) 253 << "WifiRelay::initCheck() returned error " 254 << res 255 << " (" 256 << strerror(-res) 257 << ")"; 258 259 exit(1); 260 } 261 262 #if !defined(CUTTLEFISH_HOST) 263 cvd::NlClient client(NETLINK_GENERIC); 264 if (!client.Init()) { 265 LOG(ERROR) << "Could not open Netlink Generic."; 266 exit(1); 267 } 268 269 cvd::NlClient nlRoute(NETLINK_ROUTE); 270 if (!nlRoute.Init()) { 271 LOG(ERROR) << "Could not open Netlink Route."; 272 exit(1); 273 } 274 275 std::thread([&client, &nlRoute] { 276 for (;;) { 277 fd_set rs; 278 FD_ZERO(&rs); 279 280 int fdGeneric = nl_socket_get_fd(client.Sock()); 281 int fdRoute = nl_socket_get_fd(nlRoute.Sock()); 282 283 FD_SET(fdGeneric, &rs); 284 FD_SET(fdRoute, &rs); 285 286 int maxFd = std::max(fdGeneric, fdRoute); 287 288 int res = select(maxFd + 1, &rs, nullptr, nullptr, nullptr); 289 290 if (res == 0) { 291 continue; 292 } else if (res < 0) { 293 continue; 294 } 295 296 if (FD_ISSET(fdGeneric, &rs)) { 297 nl_recvmsgs_default(client.Sock()); 298 } 299 300 if (FD_ISSET(fdRoute, &rs)) { 301 nl_recvmsgs_default(nlRoute.Sock()); 302 } 303 } 304 }).detach(); 305 306 const std::string phyName = FLAGS_iface_name + "_phy"; 307 if (createRadio(&client, relay.mac80211Family(), phyName.c_str()) < 0) { 308 LOG(ERROR) << "Could not create radio."; 309 exit(1); 310 } 311 312 int phyIndex = getPhyIndex(phyName); 313 CHECK_GE(phyIndex, 0); 314 LOG(VERBOSE) << "Got PHY index " << phyIndex; 315 316 int ifaceIndex = getInterfaceIndex( 317 &client, relay.nl80211Family(), static_cast<uint32_t>(phyIndex)); 318 319 CHECK_GE(ifaceIndex, 0); 320 LOG(VERBOSE) << "Got interface index " << ifaceIndex; 321 322 if (updateInterface( 323 &nlRoute, ifaceIndex, FLAGS_iface_name, &guestMAC[0]) < 0) { 324 LOG(ERROR) << "Failed to update interface."; 325 exit(1); 326 } 327 #endif // !defined(CUTTLEFISH_HOST) 328 329 relay.run(); 330 331 return 0; 332 } 333