1 /* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifdef HAVE_CONFIG_H 29 #include "config.h" 30 #endif 31 32 #include "talk/base/network.h" 33 #include "talk/base/stream.h" 34 35 #ifdef POSIX 36 #include <sys/socket.h> 37 #include <sys/utsname.h> 38 #include <sys/ioctl.h> 39 #include <net/if.h> 40 #include <unistd.h> 41 #include <errno.h> 42 #endif // POSIX 43 44 #ifdef WIN32 45 #include "talk/base/win32.h" 46 #include <Iphlpapi.h> 47 #endif 48 49 #include <algorithm> 50 #include <cassert> 51 #include <cfloat> 52 #include <cmath> 53 #include <cstdio> 54 #include <cstring> 55 #include <sstream> 56 57 #include "talk/base/host.h" 58 #include "talk/base/logging.h" 59 #include "talk/base/scoped_ptr.h" 60 #include "talk/base/socket.h" // includes something that makes windows happy 61 #include "talk/base/stringencode.h" 62 #include "talk/base/time.h" 63 64 namespace { 65 66 const double kAlpha = 0.5; // weight for data infinitely far in the past 67 const double kHalfLife = 2000; // half life of exponential decay (in ms) 68 const double kLog2 = 0.693147180559945309417; 69 const double kLambda = kLog2 / kHalfLife; 70 71 // assume so-so quality unless data says otherwise 72 const double kDefaultQuality = talk_base::QUALITY_FAIR; 73 74 typedef std::map<std::string, std::string> StrMap; 75 76 void BuildMap(const StrMap& map, std::string& str) { 77 str.append("{"); 78 bool first = true; 79 for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { 80 if (!first) str.append(","); 81 str.append(i->first); 82 str.append("="); 83 str.append(i->second); 84 first = false; 85 } 86 str.append("}"); 87 } 88 89 void ParseCheck(std::istringstream& ist, char ch) { 90 if (ist.get() != ch) 91 LOG(LERROR) << "Expecting '" << ch << "'"; 92 } 93 94 std::string ParseString(std::istringstream& ist) { 95 std::string str; 96 int count = 0; 97 while (ist) { 98 char ch = ist.peek(); 99 if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { 100 break; 101 } else if (ch == '{') { 102 count += 1; 103 } else if (ch == '}') { 104 count -= 1; 105 if (count < 0) 106 LOG(LERROR) << "mismatched '{' and '}'"; 107 } 108 str.append(1, static_cast<char>(ist.get())); 109 } 110 return str; 111 } 112 113 void ParseMap(const std::string& str, StrMap& map) { 114 if (str.size() == 0) 115 return; 116 std::istringstream ist(str); 117 ParseCheck(ist, '{'); 118 for (;;) { 119 std::string key = ParseString(ist); 120 ParseCheck(ist, '='); 121 std::string val = ParseString(ist); 122 map[key] = val; 123 if (ist.peek() == ',') 124 ist.get(); 125 else 126 break; 127 } 128 ParseCheck(ist, '}'); 129 if (ist.rdbuf()->in_avail() != 0) 130 LOG(LERROR) << "Unexpected characters at end"; 131 } 132 133 } // namespace 134 135 namespace talk_base { 136 137 NetworkManager::~NetworkManager() { 138 for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) 139 delete i->second; 140 } 141 142 bool NetworkManager::GetNetworks(std::vector<Network*>* result) { 143 std::vector<Network*> list; 144 if (!EnumNetworks(false, &list)) { 145 return false; 146 } 147 148 for (uint32 i = 0; i < list.size(); ++i) { 149 NetworkMap::iterator iter = networks_.find(list[i]->name()); 150 151 Network* network; 152 if (iter == networks_.end()) { 153 network = list[i]; 154 } else { 155 network = iter->second; 156 network->set_ip(list[i]->ip()); 157 network->set_gateway_ip(list[i]->gateway_ip()); 158 delete list[i]; 159 } 160 161 networks_[network->name()] = network; 162 result->push_back(network); 163 } 164 return true; 165 } 166 167 void NetworkManager::DumpNetworks(bool include_ignored) { 168 std::vector<Network*> list; 169 EnumNetworks(include_ignored, &list); 170 LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:"; 171 for (size_t i = 0; i < list.size(); ++i) { 172 const Network* network = list[i]; 173 if (!network->ignored() || include_ignored) { 174 LOG(LS_INFO) << network->ToString() << ": " << network->description() 175 << ", Gateway=" 176 << SocketAddress::IPToString(network->gateway_ip()) 177 << ((network->ignored()) ? ", Ignored" : ""); 178 } 179 } 180 } 181 182 std::string NetworkManager::GetState() const { 183 StrMap map; 184 for (NetworkMap::const_iterator i = networks_.begin(); 185 i != networks_.end(); ++i) 186 map[i->first] = i->second->GetState(); 187 188 std::string str; 189 BuildMap(map, str); 190 return str; 191 } 192 193 void NetworkManager::SetState(const std::string& str) { 194 StrMap map; 195 ParseMap(str, map); 196 197 for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { 198 std::string name = i->first; 199 std::string state = i->second; 200 201 Network* network = new Network(name, "", 0, 0); 202 network->SetState(state); 203 networks_[name] = network; 204 } 205 } 206 207 #ifdef POSIX 208 // Gets the default gateway for the specified interface. 209 uint32 GetDefaultGateway(const std::string& name) { 210 #ifdef OSX 211 // TODO: /proc/net/route doesn't exist, 212 // Use ioctl to get the routing table 213 return 0xFFFFFFFF; 214 #endif 215 216 uint32 gateway_ip = 0; 217 218 FileStream fs; 219 if (fs.Open("/proc/net/route", "r")) { 220 std::string line; 221 while (fs.ReadLine(&line) == SR_SUCCESS && gateway_ip == 0) { 222 char iface[16]; 223 unsigned int ip, gw; 224 if (sscanf(line.c_str(), "%7s %8X %8X", iface, &ip, &gw) == 3 && 225 name == iface && ip == 0) { 226 gateway_ip = ntohl(gw); 227 } 228 } 229 } 230 231 return gateway_ip; 232 } 233 234 235 bool NetworkManager::CreateNetworks(bool include_ignored, 236 std::vector<Network*>* networks) { 237 int fd; 238 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 239 LOG_ERR(LERROR) << "socket"; 240 return false; 241 } 242 243 struct ifconf ifc; 244 ifc.ifc_len = 64 * sizeof(struct ifreq); 245 ifc.ifc_buf = new char[ifc.ifc_len]; 246 247 if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { 248 LOG_ERR(LERROR) << "ioctl"; 249 return false; 250 } 251 assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq))); 252 253 struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf); 254 struct ifreq* end = 255 reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len); 256 257 while (ptr < end) { 258 struct sockaddr_in* inaddr = 259 reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr); 260 if (inaddr->sin_family == AF_INET) { 261 uint32 ip = ntohl(inaddr->sin_addr.s_addr); 262 scoped_ptr<Network> network( 263 new Network(ptr->ifr_name, ptr->ifr_name, ip, 264 GetDefaultGateway(ptr->ifr_name))); 265 network->set_ignored(IsIgnoredNetwork(*network)); 266 if (include_ignored || !network->ignored()) { 267 networks->push_back(network.release()); 268 } 269 } 270 271 #ifdef _SIZEOF_ADDR_IFREQ 272 ptr = reinterpret_cast<struct ifreq*>( 273 reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); 274 #else 275 ptr++; 276 #endif 277 } 278 279 delete [] ifc.ifc_buf; 280 close(fd); 281 return true; 282 } 283 #endif // POSIX 284 285 #ifdef WIN32 286 bool NetworkManager::CreateNetworks(bool include_ignored, 287 std::vector<Network*>* networks) { 288 IP_ADAPTER_INFO info_temp; 289 ULONG len = 0; 290 291 if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) 292 // This just means there's zero networks, which is not an error. 293 return true; 294 295 scoped_array<char> buf(new char[len]); 296 IP_ADAPTER_INFO *infos = reinterpret_cast<IP_ADAPTER_INFO *>(buf.get()); 297 DWORD ret = GetAdaptersInfo(infos, &len); 298 if (ret != NO_ERROR) { 299 LOG_ERR_EX(LS_ERROR, ret) << "GetAdaptersInfo failed"; 300 return false; 301 } 302 303 int count = 0; 304 for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { 305 // Ignore the loopback device. 306 if (info->Type == MIB_IF_TYPE_LOOPBACK) { 307 continue; 308 } 309 310 // In non-debug builds, don't transmit the network name because of 311 // privacy concerns. Transmit a number instead. 312 std::string name; 313 #ifdef _DEBUG 314 name = info->Description; 315 #else // !_DEBUG 316 std::ostringstream ost; 317 ost << count; 318 name = ost.str(); 319 count++; 320 #endif // !_DEBUG 321 322 scoped_ptr<Network> network(new Network(name, info->Description, 323 SocketAddress::StringToIP(info->IpAddressList.IpAddress.String), 324 SocketAddress::StringToIP(info->GatewayList.IpAddress.String))); 325 network->set_ignored(IsIgnoredNetwork(*network)); 326 if (include_ignored || !network->ignored()) { 327 networks->push_back(network.release()); 328 } 329 } 330 331 return true; 332 } 333 #endif // WIN32 334 335 bool NetworkManager::IsIgnoredNetwork(const Network& network) { 336 #ifdef POSIX 337 // Ignore local networks (lo, lo0, etc) 338 // Also filter out VMware interfaces, typically named vmnet1 and vmnet8 339 if (strncmp(network.name().c_str(), "lo", 2) == 0 || 340 strncmp(network.name().c_str(), "vmnet", 5) == 0) { 341 return true; 342 } 343 #elif defined(WIN32) 344 // Ignore any HOST side vmware adapters with a description like: 345 // VMware Virtual Ethernet Adapter for VMnet1 346 // but don't ignore any GUEST side adapters with a description like: 347 // VMware Accelerated AMD PCNet Adapter #2 348 if (strstr(network.description().c_str(), "VMnet") != NULL) { 349 return true; 350 } 351 #endif 352 353 // Ignore any networks with a 0.x.y.z IP 354 return (network.ip() < 0x01000000); 355 } 356 357 bool NetworkManager::EnumNetworks(bool include_ignored, 358 std::vector<Network*>* result) { 359 return CreateNetworks(include_ignored, result); 360 } 361 362 363 Network::Network(const std::string& name, const std::string& desc, 364 uint32 ip, uint32 gateway_ip) 365 : name_(name), description_(desc), ip_(ip), gateway_ip_(gateway_ip), 366 ignored_(false), uniform_numerator_(0), uniform_denominator_(0), 367 exponential_numerator_(0), exponential_denominator_(0), 368 quality_(kDefaultQuality) { 369 last_data_time_ = Time(); 370 371 // TODO: seed the historical data with one data point based 372 // on the link speed metric from XP (4.0 if < 50, 3.0 otherwise). 373 } 374 375 void Network::StartSession(NetworkSession* session) { 376 assert(std::find(sessions_.begin(), sessions_.end(), session) == 377 sessions_.end()); 378 sessions_.push_back(session); 379 } 380 381 void Network::StopSession(NetworkSession* session) { 382 SessionList::iterator iter = 383 std::find(sessions_.begin(), sessions_.end(), session); 384 if (iter != sessions_.end()) 385 sessions_.erase(iter); 386 } 387 388 void Network::EstimateQuality() { 389 uint32 now = Time(); 390 391 // Add new data points for the current time. 392 for (uint32 i = 0; i < sessions_.size(); ++i) { 393 if (sessions_[i]->HasQuality()) 394 AddDataPoint(now, sessions_[i]->GetCurrentQuality()); 395 } 396 397 // Construct the weighted average using both uniform and exponential weights. 398 399 double exp_shift = exp(-kLambda * (now - last_data_time_)); 400 double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; 401 double denominator = uniform_denominator_ + exp_shift * 402 exponential_denominator_; 403 404 if (denominator < DBL_EPSILON) 405 quality_ = kDefaultQuality; 406 else 407 quality_ = numerator / denominator; 408 } 409 410 std::string Network::ToString() const { 411 std::stringstream ss; 412 // Print out the first space-terminated token of the network desc, plus 413 // the IP address. 414 ss << "Net[" << description_.substr(0, description_.find(' ')) 415 << ":" << SocketAddress::IPToString(ip_) << "]"; 416 return ss.str(); 417 } 418 419 void Network::AddDataPoint(uint32 time, double quality) { 420 uniform_numerator_ += kAlpha * quality; 421 uniform_denominator_ += kAlpha; 422 423 double exp_shift = exp(-kLambda * (time - last_data_time_)); 424 exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * 425 exponential_numerator_; 426 exponential_denominator_ = (1 - kAlpha) + exp_shift * 427 exponential_denominator_; 428 429 last_data_time_ = time; 430 } 431 432 std::string Network::GetState() const { 433 StrMap map; 434 map["lt"] = talk_base::ToString<uint32>(last_data_time_); 435 map["un"] = talk_base::ToString<double>(uniform_numerator_); 436 map["ud"] = talk_base::ToString<double>(uniform_denominator_); 437 map["en"] = talk_base::ToString<double>(exponential_numerator_); 438 map["ed"] = talk_base::ToString<double>(exponential_denominator_); 439 440 std::string str; 441 BuildMap(map, str); 442 return str; 443 } 444 445 void Network::SetState(const std::string& str) { 446 StrMap map; 447 ParseMap(str, map); 448 449 last_data_time_ = FromString<uint32>(map["lt"]); 450 uniform_numerator_ = FromString<double>(map["un"]); 451 uniform_denominator_ = FromString<double>(map["ud"]); 452 exponential_numerator_ = FromString<double>(map["en"]); 453 exponential_denominator_ = FromString<double>(map["ed"]); 454 } 455 456 } // namespace talk_base 457