1 /* 2 * Copyright (C) 2016 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_system/interface_tool.h" 18 19 #include <net/if_arp.h> 20 #include <netinet/in.h> 21 #include <sys/socket.h> 22 23 #include <linux/ethtool.h> 24 /* We need linux/if.h for flags like IFF_UP. Sadly, it forward declares 25 struct sockaddr and must be included after sys/socket.h. */ 26 #include <linux/if.h> 27 28 #include <android-base/logging.h> 29 #include <android-base/unique_fd.h> 30 31 namespace android { 32 namespace wifi_system { 33 namespace { 34 35 const char kWlan0InterfaceName[] = "wlan0"; 36 37 bool GetIfState(const char* if_name, int sock, struct ifreq* ifr) { 38 memset(ifr, 0, sizeof(*ifr)); 39 if (strlcpy(ifr->ifr_name, if_name, sizeof(ifr->ifr_name)) >= 40 sizeof(ifr->ifr_name)) { 41 LOG(ERROR) << "Interface name is too long: " << if_name; 42 return false; 43 } 44 45 if (TEMP_FAILURE_RETRY(ioctl(sock, SIOCGIFFLAGS, ifr)) != 0) { 46 LOG(ERROR) << "Could not read interface state for " << if_name 47 << " (" << strerror(errno) << ")"; 48 return false; 49 } 50 51 return true; 52 } 53 54 } // namespace 55 56 bool InterfaceTool::GetUpState(const char* if_name) { 57 base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)); 58 if (sock.get() < 0) { 59 LOG(ERROR) << "Failed to open socket to set up/down state (" 60 << strerror(errno) << ")"; 61 return false; 62 } 63 64 struct ifreq ifr; 65 if (!GetIfState(if_name, sock.get(), &ifr)) { 66 return false; // logging done internally 67 } 68 69 return ifr.ifr_flags & IFF_UP; 70 } 71 72 bool InterfaceTool::SetUpState(const char* if_name, bool request_up) { 73 base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)); 74 if (sock.get() < 0) { 75 LOG(ERROR) << "Failed to open socket to set up/down state (" 76 << strerror(errno) << ")"; 77 return false; 78 } 79 80 struct ifreq ifr; 81 if (!GetIfState(if_name, sock.get(), &ifr)) { 82 return false; // logging done internally 83 } 84 85 const bool currently_up = ifr.ifr_flags & IFF_UP; 86 if (currently_up == request_up) { 87 return true; 88 } 89 90 if (request_up) { 91 ifr.ifr_flags |= IFF_UP; 92 } else { 93 ifr.ifr_flags &= ~IFF_UP; 94 } 95 96 if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFFLAGS, &ifr)) != 0) { 97 LOG(ERROR) << "Could not set interface flags for " << if_name 98 << " (" << strerror(errno) << ")"; 99 return false; 100 } 101 102 return true; 103 } 104 105 bool InterfaceTool::SetWifiUpState(bool request_up) { 106 return SetUpState(kWlan0InterfaceName, request_up); 107 } 108 109 bool InterfaceTool::SetMacAddress(const char* if_name, 110 const std::array<uint8_t, ETH_ALEN>& new_address) { 111 struct ifreq ifr; 112 static_assert(ETH_ALEN <= sizeof(ifr.ifr_hwaddr.sa_data), 113 "new address is too long"); 114 115 base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)); 116 if (sock.get() < 0) { 117 LOG(ERROR) << "Failed to open socket to set MAC address (" 118 << strerror(errno) << ")"; 119 return false; 120 } 121 122 if (!GetIfState(if_name, sock.get(), &ifr)) { 123 return false; // logging done internally 124 } 125 126 memset(&ifr.ifr_hwaddr, 0, sizeof(ifr.ifr_hwaddr)); 127 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; 128 memcpy(ifr.ifr_hwaddr.sa_data, new_address.data(), new_address.size()); 129 if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCSIFHWADDR, &ifr)) != 0) { 130 LOG(ERROR) << "Could not set interface MAC address for " << if_name 131 << " (" << strerror(errno) << ")"; 132 return false; 133 } 134 135 return true; 136 } 137 138 std::array<uint8_t, ETH_ALEN> InterfaceTool::GetFactoryMacAddress(const char* if_name) { 139 std::array<uint8_t, ETH_ALEN> paddr = {}; 140 struct ifreq ifr; 141 struct ethtool_perm_addr *epaddr; 142 143 base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)); 144 if (sock.get() < 0) { 145 LOG(ERROR) << "Failed to open socket to get factory MAC address (" 146 << strerror(errno) << ")"; 147 return paddr; 148 } 149 150 if (!GetIfState(if_name, sock.get(), &ifr)) { 151 return paddr; // logging done internally 152 } 153 154 epaddr = (ethtool_perm_addr*) malloc(sizeof(struct ethtool_perm_addr) + ETH_ALEN); 155 if (!epaddr) { 156 LOG(ERROR) << "Failed to set memory for mac address (" 157 << strerror(errno) << ")"; 158 return paddr; 159 } 160 161 epaddr->cmd = ETHTOOL_GPERMADDR; 162 epaddr->size = ETH_ALEN; 163 ifr.ifr_data = epaddr; 164 165 if (TEMP_FAILURE_RETRY(ioctl(sock.get(), SIOCETHTOOL, &ifr)) != 0) { 166 LOG(ERROR) << "Could not get factory address MAC for " << if_name 167 << " (" << strerror(errno) << ")"; 168 } else if (epaddr->size == ETH_ALEN) { 169 memcpy(paddr.data(), epaddr->data, ETH_ALEN); 170 } 171 return paddr; 172 } 173 174 } // namespace wifi_system 175 } // namespace android 176