/* * libjingle * Copyright 2004--2005, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "talk/base/host.h" #include "talk/base/logging.h" #include "talk/base/network.h" #include "talk/base/socket.h" // this includes something that makes windows happy #include "talk/base/jtime.h" #include "talk/base/basicdefs.h" #include #include #include #include #include #ifdef POSIX extern "C" { #include #include #include #include #include } #endif // POSIX #ifdef WIN32 #include #endif namespace { const double kAlpha = 0.5; // weight for data infinitely far in the past const double kHalfLife = 2000; // half life of exponential decay (in ms) const double kLog2 = 0.693147180559945309417; const double kLambda = kLog2 / kHalfLife; // assume so-so quality unless data says otherwise const double kDefaultQuality = cricket::QUALITY_FAIR; typedef std::map StrMap; void BuildMap(const StrMap& map, std::string& str) { str.append("{"); bool first = true; for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) { if (!first) str.append(","); str.append(i->first); str.append("="); str.append(i->second); first = false; } str.append("}"); } void ParseCheck(std::istringstream& ist, char ch) { if (ist.get() != ch) LOG(LERROR) << "Expecting '" << ch << "'"; } std::string ParseString(std::istringstream& ist) { std::string str; int count = 0; while (ist) { char ch = ist.peek(); if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) { break; } else if (ch == '{') { count += 1; } else if (ch == '}') { count -= 1; if (count < 0) LOG(LERROR) << "mismatched '{' and '}'"; } str.append(1, static_cast(ist.get())); } return str; } void ParseMap(const std::string& str, StrMap& map) { if (str.size() == 0) return; std::istringstream ist(str); ParseCheck(ist, '{'); for (;;) { std::string key = ParseString(ist); ParseCheck(ist, '='); std::string val = ParseString(ist); map[key] = val; if (ist.peek() == ',') ist.get(); else break; } ParseCheck(ist, '}'); if (ist.rdbuf()->in_avail() != 0) LOG(LERROR) << "Unexpected characters at end"; } #if 0 const std::string TEST_MAP0_IN = ""; const std::string TEST_MAP0_OUT = "{}"; const std::string TEST_MAP1 = "{a=12345}"; const std::string TEST_MAP2 = "{a=12345,b=67890}"; const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}"; const std::string TEST_MAP4 = "{a={d=12345,e=67890}}"; const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}"; const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}"; const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}"; class MyTest { public: MyTest() { test(TEST_MAP0_IN, TEST_MAP0_OUT); test(TEST_MAP1, TEST_MAP1); test(TEST_MAP2, TEST_MAP2); test(TEST_MAP3, TEST_MAP3); test(TEST_MAP4, TEST_MAP4); test(TEST_MAP5, TEST_MAP5); test(TEST_MAP6, TEST_MAP6); test(TEST_MAP7, TEST_MAP7); } void test(const std::string& input, const std::string& exp_output) { StrMap map; ParseMap(input, map); std::string output; BuildMap(map, output); LOG(INFO) << " ******** " << (output == exp_output); } }; static MyTest myTest; #endif template std::string ToString(T val) { std::ostringstream ost; ost << val; return ost.str(); } template T FromString(std::string str) { std::istringstream ist(str); T val; ist >> val; return val; } } namespace cricket { #ifdef POSIX void NetworkManager::CreateNetworks(std::vector& networks) { int fd; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { PLOG(LERROR, errno) << "socket"; return; } struct ifconf ifc; ifc.ifc_len = 64 * sizeof(struct ifreq); ifc.ifc_buf = new char[ifc.ifc_len]; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { PLOG(LERROR, errno) << "ioctl"; return; } assert(ifc.ifc_len < static_cast(64 * sizeof(struct ifreq))); struct ifreq* ptr = reinterpret_cast(ifc.ifc_buf); struct ifreq* end = reinterpret_cast(ifc.ifc_buf + ifc.ifc_len); while (ptr < end) { struct sockaddr_in* inaddr = reinterpret_cast(&ptr->ifr_ifru.ifru_addr); if (inaddr->sin_family == AF_INET) { uint32 ip = ntohl(inaddr->sin_addr.s_addr); networks.push_back(new Network(std::string(ptr->ifr_name), ip)); } #ifdef _SIZEOF_ADDR_IFREQ ptr = reinterpret_cast( reinterpret_cast(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); #else ptr++; #endif } delete [] ifc.ifc_buf; close(fd); } #endif #ifdef WIN32 void NetworkManager::CreateNetworks(std::vector& networks) { IP_ADAPTER_INFO info_temp; ULONG len = 0; if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW) return; IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len]; if (GetAdaptersInfo(infos, &len) != NO_ERROR) return; int count = 0; for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) { if (info->Type == MIB_IF_TYPE_LOOPBACK) continue; if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0) continue; // In production, don't transmit the network name because of // privacy concerns. Transmit a number instead. std::string name; #if defined(PRODUCTION) std::ostringstream ost; ost << count; name = ost.str(); count++; #else name = info->Description; #endif networks.push_back(new Network(name, SocketAddress::StringToIP(info->IpAddressList.IpAddress.String))); } delete infos; } #endif void NetworkManager::GetNetworks(std::vector& result) { std::vector list; CreateNetworks(list); for (uint32 i = 0; i < list.size(); ++i) { NetworkMap::iterator iter = networks_.find(list[i]->name()); Network* network; if (iter == networks_.end()) { network = list[i]; } else { network = iter->second; network->set_ip(list[i]->ip()); delete list[i]; } networks_[network->name()] = network; result.push_back(network); } } std::string NetworkManager::GetState() { StrMap map; for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i) map[i->first] = i->second->GetState(); std::string str; BuildMap(map, str); return str; } void NetworkManager::SetState(std::string str) { StrMap map; ParseMap(str, map); for (StrMap::iterator i = map.begin(); i != map.end(); ++i) { std::string name = i->first; std::string state = i->second; Network* network = new Network(name, 0); network->SetState(state); networks_[name] = network; } } Network::Network(const std::string& name, uint32 ip) : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0), exponential_numerator_(0), exponential_denominator_(0), quality_(kDefaultQuality) { last_data_time_ = Time(); // TODO: seed the historical data with one data point based on the link speed // metric from XP (4.0 if < 50, 3.0 otherwise). } void Network::StartSession(NetworkSession* session) { assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end()); sessions_.push_back(session); } void Network::StopSession(NetworkSession* session) { SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session); if (iter != sessions_.end()) sessions_.erase(iter); } void Network::EstimateQuality() { uint32 now = Time(); // Add new data points for the current time. for (uint32 i = 0; i < sessions_.size(); ++i) { if (sessions_[i]->HasQuality()) AddDataPoint(now, sessions_[i]->GetCurrentQuality()); } // Construct the weighted average using both uniform and exponential weights. double exp_shift = exp(-kLambda * (now - last_data_time_)); double numerator = uniform_numerator_ + exp_shift * exponential_numerator_; double denominator = uniform_denominator_ + exp_shift * exponential_denominator_; if (denominator < DBL_EPSILON) quality_ = kDefaultQuality; else quality_ = numerator / denominator; } void Network::AddDataPoint(uint32 time, double quality) { uniform_numerator_ += kAlpha * quality; uniform_denominator_ += kAlpha; double exp_shift = exp(-kLambda * (time - last_data_time_)); exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_; exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_; last_data_time_ = time; } std::string Network::GetState() { StrMap map; map["lt"] = ToString(last_data_time_); map["un"] = ToString(uniform_numerator_); map["ud"] = ToString(uniform_denominator_); map["en"] = ToString(exponential_numerator_); map["ed"] = ToString(exponential_denominator_); std::string str; BuildMap(map, str); return str; } void Network::SetState(std::string str) { StrMap map; ParseMap(str, map); last_data_time_ = FromString(map["lt"]); uniform_numerator_ = FromString(map["un"]); uniform_denominator_ = FromString(map["ud"]); exponential_numerator_ = FromString(map["en"]); exponential_denominator_ = FromString(map["ed"]); } } // namespace cricket