| // Copyright 2023 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "wifi/wifi_facade.h" | |
| #include <memory> | |
| #include "netsim-daemon/src/ffi.rs.h" | |
| #include "netsim/config.pb.h" | |
| #include "rust/cxx.h" | |
| #include "util/log.h" | |
| #include "util/string_utils.h" | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| #include "android-qemu2-glue/emulation/WifiService.h" | |
| #include "android-qemu2-glue/netsim/libslirp_driver.h" | |
| #endif | |
| namespace netsim::wifi { | |
| namespace { | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| std::shared_ptr<android::qemu2::WifiService> wifi_service; | |
| #endif | |
| ; | |
| } // namespace | |
| namespace facade { | |
| size_t HandleWifiCallback(const uint8_t *buf, size_t size) { | |
| // Broadcast the response to all WiFi chips. | |
| std::vector<uint8_t> packet(buf, buf + size); | |
| rust::Slice<const uint8_t> packet_slice(packet.data(), packet.size()); | |
| wifi::facade::HandleWiFiResponse(packet_slice); | |
| return size; | |
| } | |
| void Start(const rust::Slice<::std::uint8_t const> proto_bytes) { | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| // Initialize hostapd and slirp inside WiFi Service. | |
| config::WiFi config; | |
| config.ParseFromArray(proto_bytes.data(), proto_bytes.size()); | |
| android::qemu2::HostapdOptions hostapd = { | |
| .disabled = config.hostapd_options().disabled(), | |
| .ssid = config.hostapd_options().ssid(), | |
| .passwd = config.hostapd_options().passwd()}; | |
| auto host_dns = stringutils::Split(config.slirp_options().host_dns(), ","); | |
| android::qemu2::SlirpOptions slirpOpts = { | |
| .disabled = config.slirp_options().disabled(), | |
| .ipv4 = (config.slirp_options().has_ipv4() ? config.slirp_options().ipv4() | |
| : true), | |
| .restricted = config.slirp_options().restricted(), | |
| .vnet = config.slirp_options().vnet(), | |
| .vhost = config.slirp_options().vhost(), | |
| .vmask = config.slirp_options().vmask(), | |
| .ipv6 = (config.slirp_options().has_ipv6() ? config.slirp_options().ipv6() | |
| : true), | |
| .vprefix6 = config.slirp_options().vprefix6(), | |
| .vprefixLen = (uint8_t)config.slirp_options().vprefixlen(), | |
| .vhost6 = config.slirp_options().vhost6(), | |
| .vhostname = config.slirp_options().vhostname(), | |
| .tftpath = config.slirp_options().tftpath(), | |
| .bootfile = config.slirp_options().bootfile(), | |
| .dhcpstart = config.slirp_options().dhcpstart(), | |
| .dns = config.slirp_options().dns(), | |
| .dns6 = config.slirp_options().dns6(), | |
| .host_dns = host_dns, | |
| }; | |
| if (!config.slirp_options().host_dns().empty()) { | |
| BtsLogInfo("Host DNS server: %s", | |
| config.slirp_options().host_dns().c_str()); | |
| } | |
| auto builder = android::qemu2::WifiService::Builder() | |
| .withHostapd(hostapd) | |
| .withSlirp(slirpOpts) | |
| .withOnReceiveCallback(HandleWifiCallback) | |
| .withVerboseLogging(true); | |
| wifi_service = builder.build(); | |
| if (!wifi_service->init()) { | |
| BtsLogWarn("Failed to initialize wifi service"); | |
| } | |
| #endif | |
| } | |
| void Stop() { | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| wifi_service->stop(); | |
| #endif | |
| } | |
| } // namespace facade | |
| void libslirp_main_loop_wait() { | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| // main_loop_wait is a non-blocking call where fds maintained by the | |
| // WiFi service (slirp) are polled and serviced for I/O. When any fd | |
| // become ready for I/O, slirp_pollfds_poll() will be invoked to read | |
| // from the open sockets therefore incoming packets are serviced. | |
| android::qemu2::libslirp_main_loop_wait(true); | |
| #endif | |
| } | |
| void HandleWifiRequestCxx(uint32_t chip_id, const rust::Vec<uint8_t> &packet) { | |
| #ifdef NETSIM_ANDROID_EMULATOR | |
| // Send the packet to the WiFi service. | |
| struct iovec iov[1]; | |
| iov[0].iov_base = (void *)packet.data(); | |
| iov[0].iov_len = packet.size(); | |
| wifi_service->send(android::base::IOVector(iov, iov + 1)); | |
| #endif | |
| } | |
| } // namespace netsim::wifi |