| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Provides MAC addresses of connected routers by using the /proc/net/ | |
| 6 // directory which contains files with network information. | |
| 7 // This directory is used in most Linux based operating systems. | |
| 8 | |
| 9 #include "content/browser/geolocation/gateway_data_provider_linux.h" | |
| 10 | |
| 11 #include <algorithm> | |
| 12 #include <arpa/inet.h> | |
| 13 #include <net/if.h> | |
| 14 #include <net/if_arp.h> | |
| 15 #include <set> | |
| 16 #include <string> | |
| 17 #include <sys/ioctl.h> | |
| 18 #include <vector> | |
| 19 | |
| 20 #include "base/command_line.h" | |
| 21 #include "base/file_util.h" | |
| 22 #include "base/string_number_conversions.h" | |
| 23 #include "base/string_tokenizer.h" | |
| 24 #include "base/string_util.h" | |
| 25 #include "base/utf_string_conversions.h" | |
| 26 #include "chrome/common/chrome_switches.h" | |
| 27 #include "content/browser/geolocation/empty_device_data_provider.h" | |
| 28 #include "content/browser/geolocation/gateway_data_provider_common.h" | |
| 29 | |
| 30 namespace { | |
| 31 const unsigned int kMaxArpIterations = 30; | |
| 32 const unsigned int kMaxRouteIterations = 20; | |
| 33 const char kNoGateway[] = "00000000"; | |
| 34 const char kArpFilePath[] = "/proc/net/arp"; | |
| 35 const char kRouteFilePath[] = "/proc/net/route"; | |
| 36 | |
| 37 // Converts an IP address held in a little endian paired hex format to period | |
| 38 // separated decimal format. For example 0101A8C0 becomes 192.168.1.1 | |
| 39 std::string HexToIpv4Format(std::string& hex_address) { | |
| 40 if (hex_address.size() != 8) | |
| 41 return ""; | |
| 42 std::vector<uint8> bytes; | |
| 43 if (!base::HexStringToBytes(hex_address, &bytes) || | |
| 44 bytes.size() != 4) { | |
| 45 return ""; | |
| 46 } | |
| 47 struct in_addr in; | |
| 48 uint32 ip_native_endian; | |
| 49 memcpy(&ip_native_endian, &bytes[0], 4); | |
| 50 in.s_addr = htonl(ip_native_endian); | |
| 51 return inet_ntoa(in); | |
| 52 } | |
| 53 | |
| 54 // TODO(joth): Cache the sets of gateways and MAC addresses to avoid reading | |
| 55 // through the arp table when the routing table hasn't changed. | |
| 56 class LinuxGatewayApi : public GatewayDataProviderCommon::GatewayApiInterface { | |
| 57 public: | |
| 58 LinuxGatewayApi() {} | |
| 59 virtual ~LinuxGatewayApi() {} | |
| 60 | |
| 61 static LinuxGatewayApi* Create() { | |
| 62 return new LinuxGatewayApi(); | |
| 63 } | |
| 64 | |
| 65 // GatewayApiInterface | |
| 66 virtual bool GetRouterData(GatewayData::RouterDataSet* data); | |
| 67 | |
| 68 private: | |
| 69 DISALLOW_COPY_AND_ASSIGN(LinuxGatewayApi); | |
| 70 }; | |
| 71 | |
| 72 // Gets a set of gateways using data from /proc/net/route | |
| 73 // Returns false if we cannot read the route file or we cannot classify the | |
| 74 // type of network adapter. | |
| 75 // Routing data is held in the following format | |
| 76 // Note: "|" represents a delimeter. | |
| 77 // Iface|Destination|Gateway|Flags|RefCnt|Use|Metric|Mask|MTU|Window|IRTT | |
| 78 // The delimiter for this table is "\t". | |
| 79 // ioctl calls are used to verify the type of network adapter, using interface | |
| 80 // names from the route table. | |
| 81 bool GetGateways(std::set<std::string>* gateways) { | |
| 82 FilePath route_file_path(kRouteFilePath); | |
| 83 std::string route_file; | |
| 84 if (!file_util::ReadFileToString(route_file_path, &route_file)) { | |
| 85 // Unable to read file. | |
| 86 return false; | |
| 87 } | |
| 88 StringTokenizer tokenized_route_file(route_file, "\n"); | |
| 89 std::vector<std::string> route_data; | |
| 90 // Remove column labels. | |
| 91 tokenized_route_file.GetNext(); | |
| 92 | |
| 93 // Get rows of data. | |
| 94 while (tokenized_route_file.GetNext() && | |
| 95 route_data.size() < kMaxRouteIterations) | |
| 96 route_data.push_back(tokenized_route_file.token()); | |
| 97 | |
| 98 if (route_data.empty()) { | |
| 99 // Having no data is not an error case. | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 103 int sock; | |
| 104 sock = socket(AF_INET, SOCK_STREAM, 0); | |
| 105 if (sock == -1) | |
| 106 return false; | |
| 107 struct ifreq network_device; | |
| 108 | |
| 109 for (size_t j = 0; j < route_data.size(); ++j) { | |
| 110 // Get gateway address. | |
| 111 std::string gateway; | |
| 112 StringTokenizer tokenized_route_data(route_data[j], "\t"); | |
| 113 // Check if device is an Ethernet device. | |
| 114 if (!tokenized_route_data.GetNext()) // Get Iface. | |
| 115 continue; | |
| 116 // Ensure space for the null term too. | |
| 117 if (tokenized_route_data.token().size() >= | |
| 118 arraysize(network_device.ifr_name)) | |
| 119 continue; | |
| 120 memset(&network_device, 0, sizeof(network_device)); | |
| 121 base::strlcpy(network_device.ifr_name, | |
| 122 tokenized_route_data.token().c_str(), | |
| 123 arraysize(network_device.ifr_name)); | |
| 124 if (ioctl(sock, SIOCGIFHWADDR, | |
| 125 reinterpret_cast<char*>(&network_device)) < 0) { | |
| 126 // Could not get device type. | |
| 127 continue; | |
| 128 } | |
| 129 if (network_device.ifr_hwaddr.sa_family != ARPHRD_ETHER) { | |
| 130 // Device is not an Ethernet device. | |
| 131 continue; | |
| 132 } | |
| 133 if (!tokenized_route_data.GetNext()) // Skip Destination. | |
| 134 continue; | |
| 135 if (!tokenized_route_data.GetNext()) // Get Gateway. | |
| 136 continue; | |
| 137 gateway = tokenized_route_data.token(); | |
| 138 if (gateway != kNoGateway) { | |
| 139 // TODO(joth): Currently only works for gateways using IPv4. | |
| 140 std::string gateway_int_format = HexToIpv4Format(gateway); | |
| 141 if (!gateway_int_format.empty()) | |
| 142 gateways->insert(gateway_int_format); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 close(sock); | |
| 147 return true; | |
| 148 } | |
| 149 | |
| 150 // Gets a RouterDataSet containing MAC addresses related to any connected | |
| 151 // routers. Returns false if we cannot read the arp file. | |
| 152 // The /proc/net/arp file contains arp data in the following format | |
| 153 // Note: "|" represents a delimeter. | |
| 154 // IP address|HW type|Flags|HW address|Mask|Device | |
| 155 // The delimiter for this table is " ". | |
| 156 bool GetMacAddresses(GatewayData::RouterDataSet* data, | |
| 157 std::set<std::string>* gateways) { | |
| 158 // Find MAC addresses of routers | |
| 159 FilePath arp_file_path(kArpFilePath); | |
| 160 std::string arp_file_out; | |
| 161 if (!file_util::ReadFileToString(arp_file_path, &arp_file_out)) { | |
| 162 // Unable to read file. | |
| 163 return false; | |
| 164 } | |
| 165 StringTokenizer tokenized_arp_file(arp_file_out, "\n"); | |
| 166 std::vector<std::string> arp_data; | |
| 167 // Remove column labels. | |
| 168 tokenized_arp_file.GetNext(); | |
| 169 | |
| 170 // Get rows of data. | |
| 171 while (tokenized_arp_file.GetNext() && arp_data.size() < kMaxArpIterations) | |
| 172 arp_data.push_back(tokenized_arp_file.token()); | |
| 173 | |
| 174 for (size_t k = 0; k < arp_data.size(); k++) { | |
| 175 std::string ip_address; | |
| 176 StringTokenizer tokenized_arp_data(arp_data[k], " "); | |
| 177 if (!tokenized_arp_data.GetNext()) // Get IP address. | |
| 178 continue; | |
| 179 ip_address = tokenized_arp_data.token(); | |
| 180 if (gateways->find(ip_address) == gateways->end()) | |
| 181 continue; | |
| 182 if (!tokenized_arp_data.GetNext()) // Skip HW type. | |
| 183 continue; | |
| 184 if (!tokenized_arp_data.GetNext()) // Skip flags. | |
| 185 continue; | |
| 186 if (!tokenized_arp_data.GetNext()) // Get HW address. | |
| 187 continue; | |
| 188 RouterData router; | |
| 189 std::string mac_add; | |
| 190 mac_add = tokenized_arp_data.token(); | |
| 191 std::replace(mac_add.begin(), mac_add.end(), ':', '-'); | |
| 192 router.mac_address = UTF8ToUTF16(mac_add); | |
| 193 data->insert(router); | |
| 194 } | |
| 195 | |
| 196 return true; | |
| 197 } | |
| 198 | |
| 199 bool LinuxGatewayApi::GetRouterData(GatewayData::RouterDataSet* data) { | |
| 200 std::set<std::string> gateways; | |
| 201 if (!GetGateways(&gateways)) | |
| 202 return false; | |
| 203 if (gateways.empty()) | |
| 204 return true; | |
| 205 return GetMacAddresses(data, &gateways); | |
| 206 } | |
| 207 } // namespace | |
| 208 | |
| 209 // static | |
| 210 template<> | |
| 211 GatewayDataProviderImplBase* GatewayDataProvider::DefaultFactoryFunction() { | |
| 212 if (!CommandLine::ForCurrentProcess() | |
| 213 ->HasSwitch(switches::kExperimentalLocationFeatures)) | |
| 214 return new EmptyDeviceDataProvider<GatewayData>(); | |
| 215 return new GatewayDataProviderLinux(); | |
| 216 } | |
| 217 | |
| 218 GatewayDataProviderLinux::GatewayDataProviderLinux() { | |
| 219 } | |
| 220 | |
| 221 GatewayDataProviderLinux::~GatewayDataProviderLinux() { | |
| 222 } | |
| 223 | |
| 224 GatewayDataProviderCommon::GatewayApiInterface* | |
| 225 GatewayDataProviderLinux::NewGatewayApi() { | |
| 226 return LinuxGatewayApi::Create(); | |
| 227 } | |
| OLD | NEW |