diff options
Diffstat (limited to 'kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp')
-rw-r--r-- | kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp new file mode 100644 index 00000000..21b3a08f --- /dev/null +++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cpp @@ -0,0 +1,382 @@ +/* + * 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 <algorithm> +#include <cassert> +#include <cfloat> +#include <cmath> +#include <sstream> + +#ifdef POSIX +extern "C" { +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +} +#endif // POSIX + +#ifdef WIN32 +#include <Iphlpapi.h> +#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<std::string,std::string> 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<char>(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 <typename T> +std::string ToString(T val) { + std::ostringstream ost; + ost << val; + return ost.str(); +} + +template <typename T> +T FromString(std::string str) { + std::istringstream ist(str); + T val; + ist >> val; + return val; +} + +} + +namespace cricket { + +#ifdef POSIX +void NetworkManager::CreateNetworks(std::vector<Network*>& 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<int>(64 * sizeof(struct ifreq))); + + struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf); + struct ifreq* end = + reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len); + + while (ptr < end) { + struct sockaddr_in* inaddr = + reinterpret_cast<struct sockaddr_in*>(&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<struct ifreq*>( + reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr)); +#else + ptr++; +#endif + } + + delete [] ifc.ifc_buf; + close(fd); +} +#endif + +#ifdef WIN32 +void NetworkManager::CreateNetworks(std::vector<Network*>& 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<Network*>& result) { + std::vector<Network*> 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<uint32>(last_data_time_); + map["un"] = ToString<double>(uniform_numerator_); + map["ud"] = ToString<double>(uniform_denominator_); + map["en"] = ToString<double>(exponential_numerator_); + map["ed"] = ToString<double>(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<uint32>(map["lt"]); + uniform_numerator_ = FromString<double>(map["un"]); + uniform_denominator_ = FromString<double>(map["ud"]); + exponential_numerator_ = FromString<double>(map["en"]); + exponential_denominator_ = FromString<double>(map["ed"]); +} + +} // namespace cricket |