summaryrefslogtreecommitdiffstats
path: root/libwifi_system
diff options
context:
space:
mode:
authorChristopher Wiley <wiley@google.com>2016-07-14 16:40:58 -0700
committerChristopher Wiley <wiley@google.com>2016-07-18 13:58:21 -0700
commit944db7cbdafa91279b40dd8a7737307e49cf711e (patch)
tree3b6cb1aef535ccb223437a8b27a622d14de0a0d2 /libwifi_system
parent3efd47ff5e926cc1fa042e288e129df10fa4ec83 (diff)
downloadframeworks_opt_net_wifi-944db7cbdafa91279b40dd8a7737307e49cf711e.tar.gz
frameworks_opt_net_wifi-944db7cbdafa91279b40dd8a7737307e49cf711e.tar.bz2
frameworks_opt_net_wifi-944db7cbdafa91279b40dd8a7737307e49cf711e.zip
Move hostapd management logic to libwifi-system
This is the logic that existed in netd, but moved into a mockable C++ object and formulated to support unittesting the config file generation. Bug: 30040724 Test: unit tests pass, data for tests extracted from config files generated by existing netd code. Test: netd continues to be able to set up APs for tethering using libwifi-system rather than previous code. Test: No SELinux policy violations during/after setup. Test: Can sign in to network backed by hostapd started by netd using libwifi-system. Change-Id: I1156c494fc889a3fdf14182b11c7697d93fdf586
Diffstat (limited to 'libwifi_system')
-rw-r--r--libwifi_system/Android.mk20
-rw-r--r--libwifi_system/hostapd_manager.cpp202
-rw-r--r--libwifi_system/include/wifi_system/hostapd_manager.h81
-rw-r--r--libwifi_system/tests/hostapd_manager_unittest.cpp145
-rw-r--r--libwifi_system/tests/main.cpp27
5 files changed, 475 insertions, 0 deletions
diff --git a/libwifi_system/Android.mk b/libwifi_system/Android.mk
index d37fa9a9e..61d340a23 100644
--- a/libwifi_system/Android.mk
+++ b/libwifi_system/Android.mk
@@ -34,8 +34,10 @@ LOCAL_MODULE := libwifi-system
LOCAL_CFLAGS := $(wifi_system_cflags)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbase
LOCAL_SHARED_LIBRARIES := \
libbase \
+ libcrypto \
libcutils \
liblog \
libnetutils \
@@ -49,6 +51,7 @@ LOCAL_SHARED_LIBRARIES += libwpa_client
endif
LOCAL_SRC_FILES := \
+ hostapd_manager.cpp \
interface_tool.cpp \
hal_tool.cpp \
wifi.cpp
@@ -68,4 +71,21 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/testlib/include
include $(BUILD_STATIC_LIBRARY)
+
+# Unit tests for libwifi-system
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-system_tests
+LOCAL_CPPFLAGS := $(wificond_cpp_flags)
+LOCAL_SRC_FILES := \
+ tests/main.cpp \
+ tests/hostapd_manager_unittest.cpp
+LOCAL_STATIC_LIBRARIES := \
+ libgmock \
+ libgtest
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libwifi-system
+include $(BUILD_NATIVE_TEST)
+
endif
diff --git a/libwifi_system/hostapd_manager.cpp b/libwifi_system/hostapd_manager.cpp
new file mode 100644
index 000000000..889fd6946
--- /dev/null
+++ b/libwifi_system/hostapd_manager.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 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_system/hostapd_manager.h"
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
+
+#include "wifi_system/wifi.h"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using std::string;
+using std::vector;
+using std::stringstream;
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const int kDefaultApChannel = 6;
+const char kHostapdServiceName[] = "hostapd";
+const char kHostapdConfigFilePath[] = "/data/misc/wifi/hostapd.conf";
+
+string GeneratePsk(const vector<uint8_t>& ssid,
+ const vector<uint8_t>& passphrase) {
+ string result;
+ unsigned char psk[SHA256_DIGEST_LENGTH];
+
+ // Use the PKCS#5 PBKDF2 with 4096 iterations
+ if (PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char*>(passphrase.data()),
+ passphrase.size(),
+ ssid.data(), ssid.size(),
+ 4096, sizeof(psk), psk) != 1) {
+ LOG(ERROR) << "Cannot generate PSK using PKCS#5 PBKDF2";
+ return result;
+ }
+
+ stringstream ss;
+ ss << std::hex;
+ ss << std::setfill('0');
+ for (int j = 0; j < SHA256_DIGEST_LENGTH; j++) {
+ ss << std::setw(2) << static_cast<unsigned int>(psk[j]);
+ }
+ result = ss.str();
+
+ return result;
+}
+
+} // namespace
+
+bool HostapdManager::StartHostapd() {
+ if (hostapd_is_running_) {
+ LOG(ERROR) << "SoftAP is already running";
+ return false;
+ }
+
+ if (ensure_entropy_file_exists() < 0) {
+ LOG(WARNING) << "Wi-Fi entropy file was not created";
+ }
+
+ if (property_set("ctl.start", kHostapdServiceName) != 0) {
+ LOG(ERROR) << "Failed to start SoftAP";
+ return false;
+ }
+
+ LOG(DEBUG) << "SoftAP started successfully";
+ hostapd_is_running_ = true;
+ return true;
+}
+
+bool HostapdManager::IsHostapdRunning() {
+ return hostapd_is_running_;
+}
+
+bool HostapdManager::StopHostapd() {
+ if (!hostapd_is_running_) {
+ LOG(DEBUG) << "SoftAP is not running";
+ return true; // Not really an error, hostapd is already stopped.
+ }
+
+ LOG(DEBUG) << "Stopping the SoftAP service...";
+
+ if (property_set("ctl.stop", kHostapdServiceName) < 0) {
+ LOG(ERROR) << "Failed to stop hostapd service!";
+ return false;
+ }
+
+ LOG(DEBUG) << "SoftAP stopped successfully";
+ hostapd_is_running_ = false;
+ return true;
+}
+
+bool HostapdManager::WriteHostapdConfig(const string& config) {
+ if (!WriteStringToFile(config, kHostapdConfigFilePath,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+ AID_SYSTEM, AID_WIFI)) {
+ int error = errno;
+ LOG(ERROR) << "Cannot write hostapd config to \""
+ << kHostapdConfigFilePath << "\": " << strerror(error);
+ return false;
+ }
+ return true;
+}
+
+string HostapdManager::CreateHostapdConfig(
+ const string& interface_name,
+ const vector<uint8_t> ssid,
+ bool is_hidden,
+ int channel,
+ EncryptionType encryption_type,
+ const vector<uint8_t> passphrase) {
+ string result;
+
+ if (channel < 0) {
+ channel = kDefaultApChannel;
+ }
+
+ if (ssid.size() > 32) {
+ LOG(ERROR) << "SSIDs must be <= 32 bytes long";
+ return result;
+ }
+
+ stringstream ss;
+ ss << std::hex;
+ ss << std::setfill('0');
+ for (uint8_t b : ssid) {
+ ss << std::setw(2) << static_cast<unsigned int>(b);
+ }
+ const string ssid_as_string = ss.str();
+
+ string encryption_config;
+ if (encryption_type != EncryptionType::kOpen) {
+ string psk = GeneratePsk(ssid, passphrase);
+ if (psk.empty()) {
+ return result;
+ }
+ if (encryption_type == EncryptionType::kWpa) {
+ encryption_config = StringPrintf("wpa=3\n"
+ "wpa_pairwise=TKIP CCMP\n"
+ "wpa_psk=%s\n", psk.c_str());
+ } else if (encryption_type == EncryptionType::kWpa2) {
+ encryption_config = StringPrintf("wpa=2\n"
+ "rsn_pairwise=CCMP\n"
+ "wpa_psk=%s\n", psk.c_str());
+ } else {
+ using encryption_t = std::underlying_type<EncryptionType>::type;
+ LOG(ERROR) << "Unknown encryption type ("
+ << static_cast<encryption_t>(encryption_type)
+ << ")";
+ return result;
+ }
+ }
+
+ result = StringPrintf(
+ "interface=%s\n"
+ "driver=nl80211\n"
+ "ctrl_interface=/data/misc/wifi/hostapd\n"
+ // ssid2 signals to hostapd that the value is not a literal value
+ // for use as a SSID. In this case, we're giving it a hex string
+ // and hostapd needs to expect that.
+ "ssid2=%s\n"
+ "channel=%d\n"
+ "ieee80211n=1\n"
+ "hw_mode=%c\n"
+ "ignore_broadcast_ssid=%d\n"
+ "wowlan_triggers=any\n"
+ "%s",
+ interface_name.c_str(),
+ ssid_as_string.c_str(),
+ channel,
+ (channel <= 14) ? 'g' : 'a',
+ (is_hidden) ? 1 : 0,
+ encryption_config.c_str());
+ return result;
+}
+
+} // namespace wifi_system
+} // namespace android
diff --git a/libwifi_system/include/wifi_system/hostapd_manager.h b/libwifi_system/include/wifi_system/hostapd_manager.h
new file mode 100644
index 000000000..2bffde453
--- /dev/null
+++ b/libwifi_system/include/wifi_system/hostapd_manager.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
+
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wifi_system {
+
+class HostapdManager {
+ public:
+ enum class EncryptionType {
+ kOpen,
+ kWpa,
+ kWpa2, // Strongly prefer this if at all possible.
+ };
+
+ HostapdManager() = default;
+ virtual ~HostapdManager() = default;
+
+ // Request that hostapd be started.
+ // Returns true on success.
+ virtual bool StartHostapd();
+
+ // Returns true if hostapd is currently running.
+ virtual bool IsHostapdRunning();
+
+ // Request that a running instance of hostapd be stopped.
+ // Returns true on success.
+ virtual bool StopHostapd();
+
+ // Create a string suitable for writing to the hostapd configuration file.
+ // |interface_name| is a network interface name (e.g. "wlan0").
+ // |ssid| is the SSID used by the configurated network.
+ // |is_hidden| is true iff hostapd should not broadcast the SSID.
+ // |channel| is the WiFi channel (e.g. 6) or <0 for a default value.
+ // |encryption_type| defines the encryption of the configured network.
+ // |passphrase| is ignored for kOpen networks.
+ //
+ // Returns an empty string on failure.
+ virtual std::string CreateHostapdConfig(
+ const std::string& interface_name,
+ const std::vector<uint8_t> ssid,
+ bool is_hidden,
+ int channel,
+ EncryptionType encryption,
+ const std::vector<uint8_t> passphrase);
+
+ // Write out a hostapd configuration file created via CreateHostapdConfig().
+ // Future instances of hostapd will use this new configuration.
+ // Returns true if the configuration file is successfully written.
+ virtual bool WriteHostapdConfig(const std::string& config_file);
+
+ private:
+ bool hostapd_is_running_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(HostapdManager);
+}; // class HostapdManager
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_HOSTAPD_MANAGER_H
diff --git a/libwifi_system/tests/hostapd_manager_unittest.cpp b/libwifi_system/tests/hostapd_manager_unittest.cpp
new file mode 100644
index 000000000..f8ebd995f
--- /dev/null
+++ b/libwifi_system/tests/hostapd_manager_unittest.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016, 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include "wifi_system/hostapd_manager.h"
+
+using std::string;
+using std::vector;
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kTestInterfaceName[] = "foobar0";
+const char kTestSsidStr[] = "helloisitme";
+const char kTestPassphraseStr[] = "yourelookingfor";
+const int kTestChannel = 2;
+
+#define CONFIG_COMMON_PREFIX \
+ "interface=foobar0\n" \
+ "driver=nl80211\n" \
+ "ctrl_interface=/data/misc/wifi/hostapd\n" \
+ "ssid2=68656c6c6f" "6973" "6974" "6d65\n" \
+ "channel=2\n" \
+ "ieee80211n=1\n" \
+ "hw_mode=g\n"
+
+// If you generate your config file with both the test ssid
+// and the test passphrase, you'll get this line in the config.
+#define CONFIG_PSK_LINE \
+ "wpa_psk=dffa36815281e5a6eca1910f254717fa2528681335e3bbec5966d2aa9221a66e\n"
+
+#define CONFIG_WPA_SUFFIX \
+ "wpa=3\n" \
+ "wpa_pairwise=TKIP CCMP\n" \
+ CONFIG_PSK_LINE
+
+#define CONFIG_WPA2_SUFFIX \
+ "wpa=2\n" \
+ "rsn_pairwise=CCMP\n" \
+ CONFIG_PSK_LINE
+
+const char kExpectedOpenConfig[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n";
+
+const char kExpectedWpaConfig[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n"
+ CONFIG_WPA_SUFFIX;
+
+const char kExpectedWpa2Config[] =
+ CONFIG_COMMON_PREFIX
+ "ignore_broadcast_ssid=0\n"
+ "wowlan_triggers=any\n"
+ CONFIG_WPA2_SUFFIX;
+
+class HostapdManagerTest : public ::testing::Test {
+ protected:
+ string GetConfigForEncryptionType(
+ HostapdManager::EncryptionType encryption_type) {
+ return HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ false, // not hidden
+ kTestChannel,
+ encryption_type,
+ cstr2vector(kTestPassphraseStr));
+ }
+
+ vector<uint8_t> cstr2vector(const char* data) {
+ return vector<uint8_t>(data, data + strlen(data));
+ }
+}; // class HostapdManagerTest
+
+} // namespace
+
+TEST_F(HostapdManagerTest, GeneratesCorrectOpenConfig) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kOpen);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedOpenConfig, config);
+}
+
+TEST_F(HostapdManagerTest, GeneratesCorrectWpaConfig) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kWpa);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedWpaConfig, config);
+}
+
+TEST_F(HostapdManagerTest, GeneratesCorrectWpa2Config) {
+ string config = GetConfigForEncryptionType(
+ HostapdManager::EncryptionType::kWpa2);
+ EXPECT_FALSE(config.empty());
+ EXPECT_EQ(kExpectedWpa2Config, config);
+}
+
+TEST_F(HostapdManagerTest, RespectsHiddenSetting) {
+ string config = HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ true,
+ kTestChannel,
+ HostapdManager::EncryptionType::kOpen,
+ vector<uint8_t>());
+ EXPECT_FALSE(config.find("ignore_broadcast_ssid=1\n") == string::npos);
+ EXPECT_TRUE(config.find("ignore_broadcast_ssid=0\n") == string::npos);
+}
+
+TEST_F(HostapdManagerTest, CorrectlyInfersHwMode) {
+ string config = HostapdManager().CreateHostapdConfig(
+ kTestInterfaceName,
+ cstr2vector(kTestSsidStr),
+ true,
+ 44,
+ HostapdManager::EncryptionType::kOpen,
+ vector<uint8_t>());
+ EXPECT_FALSE(config.find("hw_mode=a\n") == string::npos);
+ EXPECT_TRUE(config.find("hw_mode=g\n") == string::npos);
+}
+
+
+} // namespace wifi_system
+} // namespace android
diff --git a/libwifi_system/tests/main.cpp b/libwifi_system/tests/main.cpp
new file mode 100644
index 000000000..51e8dad29
--- /dev/null
+++ b/libwifi_system/tests/main.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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 <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ // Force ourselves to always log to stderr
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
+