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
64namespace {
65
66const double kAlpha = 0.5;  // weight for data infinitely far in the past
67const double kHalfLife = 2000;  // half life of exponential decay (in ms)
68const double kLog2 = 0.693147180559945309417;
69const double kLambda = kLog2 / kHalfLife;
70
71// assume so-so quality unless data says otherwise
72const double kDefaultQuality = talk_base::QUALITY_FAIR;
73
74typedef std::map<std::string, std::string> StrMap;
75
76void 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
89void ParseCheck(std::istringstream& ist, char ch) {
90  if (ist.get() != ch)
91    LOG(LERROR) << "Expecting '" << ch << "'";
92}
93
94std::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
113void 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
135namespace talk_base {
136
137NetworkManager::~NetworkManager() {
138  for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
139    delete i->second;
140}
141
142bool 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
167void 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
182std::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
193void 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.
209uint32 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
235bool 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
286bool 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
335bool 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
357bool NetworkManager::EnumNetworks(bool include_ignored,
358                                  std::vector<Network*>* result) {
359  return CreateNetworks(include_ignored, result);
360}
361
362
363Network::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
375void Network::StartSession(NetworkSession* session) {
376  assert(std::find(sessions_.begin(), sessions_.end(), session) ==
377         sessions_.end());
378  sessions_.push_back(session);
379}
380
381void 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
388void 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
410std::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
419void 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
432std::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
445void 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