Home | History | Annotate | Download | only in base
      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