/*
* 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/interface_tool.h"
#include <net/if_arp.h>
#include <netinet/in.h>
#include <sys/socket.h>
/* We need linux/if.h for flags like IFF_UP. Sadly, it forward declares
struct sockaddr and must be included after sys/socket.h. */
#include <linux/if.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
namespace android {
namespace wifi_system {
namespace {
const char kWlan0InterfaceName[] = "wlan0";
bool GetIfState(const char* if_name, int sock, struct ifreq* ifr) {
memset(ifr, 0, sizeof(*ifr));
if (strlcpy(ifr->ifr_name, if_name, sizeof(ifr->ifr_name)) >=
sizeof(ifr->ifr_name)) {
LOG(ERROR) << "Interface name is too long: " << if_name;
return false;
}
if (TEMP_FAILURE_RETRY(ioctl(sock, SIOCGIFFLAGS, ifr)) != 0) {
LOG(ERROR) << "Could not read interface state for " << if_name
<< " (" << strerror(errno) << ")";
return false;
}
return true;
}
} // namespace
bool InterfaceTool::GetUpState(const char* if_name) {
base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (sock.get() < 0) {
LOG(ERROR) << "Failed to open socket to set up/down state ("
<< strerror(errno) << ")";
return false;
}
struct ifreq ifr;
if (!GetIfState(if_name, sock.get(), &ifr)) {
return false; // logging done internally
}
return ifr.ifr_flags & IFF_UP;
}
bool InterfaceTool::SetUpState(const char* if_name, bool request_up) {
base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (sock.get() < 0) {
LOG(ERROR) << "Failed to open socket to set up/down state ("
<< strerror(errno) << ")";
return false;
}
struct ifreq ifr;
if (!GetIfState(if_name, sock.get(), &ifr)) {
return false; // logging done internally
}
const bool currently_up = ifr.ifr_flags & IFF_UP;
if (currently_up == request_up) {
return true;
}
if (request_up) {
ifr.ifr_flags |= IFF_UP;
} else {
ifr.ifr_flags &= ~IFF_UP;
}
if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFFLAGS, &ifr)) != 0) {
LOG(ERROR) << "Could not set interface flags for " << if_name
<< " (" << strerror(errno) << ")";
return false;
}
return true;
}
bool InterfaceTool::SetWifiUpState(bool request_up) {
return SetUpState(kWlan0InterfaceName, request_up);
}
bool InterfaceTool::SetMacAddress(const char* if_name,
const std::array<uint8_t, ETH_ALEN>& new_address) {
struct ifreq ifr;
static_assert(ETH_ALEN <= sizeof(ifr.ifr_hwaddr.sa_data),
"new address is too long");
base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (sock.get() < 0) {
LOG(ERROR) << "Failed to open socket to set MAC address ("
<< strerror(errno) << ")";
return false;
}
if (!GetIfState(if_name, sock.get(), &ifr)) {
return false; // logging done internally
}
memset(&ifr.ifr_hwaddr, 0, sizeof(ifr.ifr_hwaddr));
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
memcpy(ifr.ifr_hwaddr.sa_data, new_address.data(), new_address.size());
if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFHWADDR, &ifr)) != 0) {
LOG(ERROR) << "Could not set interface MAC address for " << if_name
<< " (" << strerror(errno) << ")";
return false;
}
return true;
}
} // namespace wifi_system
} // namespace android