Home | History | Annotate | Download | only in wifi_relay
      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