nrinterfaceprioritizer.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include <algorithm>
  5. #include <map>
  6. #include <set>
  7. #include <string>
  8. #include <vector>
  9. #include "logging.h"
  10. #include "nrinterfaceprioritizer.h"
  11. #include "nsCOMPtr.h"
  12. MOZ_MTLOG_MODULE("mtransport")
  13. namespace {
  14. class LocalAddress {
  15. public:
  16. LocalAddress()
  17. : ifname_(),
  18. addr_(),
  19. key_(),
  20. is_vpn_(-1),
  21. estimated_speed_(-1),
  22. type_preference_(-1),
  23. ip_version_(-1) {}
  24. bool Init(const nr_local_addr& local_addr) {
  25. ifname_ = local_addr.addr.ifname;
  26. char buf[MAXIFNAME + 41];
  27. int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, sizeof(buf));
  28. if (r) {
  29. MOZ_MTLOG(ML_ERROR, "Error formatting interface key.");
  30. return false;
  31. }
  32. key_ = buf;
  33. r = nr_transport_addr_get_addrstring(&local_addr.addr, buf, sizeof(buf));
  34. if (r) {
  35. MOZ_MTLOG(ML_ERROR, "Error formatting address string.");
  36. return false;
  37. }
  38. addr_ = buf;
  39. is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0;
  40. estimated_speed_ = local_addr.interface.estimated_speed;
  41. type_preference_ = GetNetworkTypePreference(local_addr.interface.type);
  42. ip_version_ = local_addr.addr.ip_version;
  43. return true;
  44. }
  45. bool operator<(const LocalAddress& rhs) const {
  46. // Interface that is "less" here is preferred.
  47. // If type preferences are different, we should simply sort by
  48. // |type_preference_|.
  49. if (type_preference_ != rhs.type_preference_) {
  50. return type_preference_ < rhs.type_preference_;
  51. }
  52. // If type preferences are the same, the next thing we use to sort is vpn.
  53. // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is
  54. // not in vpn gets priority.
  55. if (is_vpn_ != rhs.is_vpn_) {
  56. return is_vpn_ < rhs.is_vpn_;
  57. }
  58. // Compare estimated speed.
  59. if (estimated_speed_ != rhs.estimated_speed_) {
  60. return estimated_speed_ > rhs.estimated_speed_;
  61. }
  62. // See if our hard-coded pref list helps us.
  63. auto thisindex = std::find(interface_preference_list().begin(),
  64. interface_preference_list().end(),
  65. ifname_);
  66. auto rhsindex = std::find(interface_preference_list().begin(),
  67. interface_preference_list().end(),
  68. rhs.ifname_);
  69. if (thisindex != rhsindex) {
  70. return thisindex < rhsindex;
  71. }
  72. // Prefer IPV6 over IPV4
  73. if (ip_version_ != rhs.ip_version_) {
  74. return ip_version_ > rhs.ip_version_;
  75. }
  76. // Now we start getting into arbitrary stuff
  77. if (ifname_ != rhs.ifname_) {
  78. return ifname_ < rhs.ifname_;
  79. }
  80. return addr_ < rhs.addr_;
  81. }
  82. const std::string& GetKey() const {
  83. return key_;
  84. }
  85. private:
  86. // Getting the preference corresponding to a type. Getting lower number here
  87. // means the type of network is preferred.
  88. static inline int GetNetworkTypePreference(int type) {
  89. if (type & NR_INTERFACE_TYPE_WIRED) {
  90. return 1;
  91. }
  92. if (type & NR_INTERFACE_TYPE_WIFI) {
  93. return 2;
  94. }
  95. if (type & NR_INTERFACE_TYPE_MOBILE) {
  96. return 3;
  97. }
  98. return 4;
  99. }
  100. // TODO(bug 895790): Once we can get useful interface properties on Darwin,
  101. // we should remove this stuff.
  102. static const std::vector<std::string>& interface_preference_list()
  103. {
  104. static std::vector<std::string> list(build_interface_preference_list());
  105. return list;
  106. }
  107. static std::vector<std::string> build_interface_preference_list()
  108. {
  109. std::vector<std::string> result;
  110. result.push_back("rl0");
  111. result.push_back("wi0");
  112. result.push_back("en0");
  113. result.push_back("enp2s0");
  114. result.push_back("enp3s0");
  115. result.push_back("en1");
  116. result.push_back("en2");
  117. result.push_back("en3");
  118. result.push_back("eth0");
  119. result.push_back("eth1");
  120. result.push_back("eth2");
  121. result.push_back("em1");
  122. result.push_back("em0");
  123. result.push_back("ppp");
  124. result.push_back("ppp0");
  125. result.push_back("vmnet1");
  126. result.push_back("vmnet0");
  127. result.push_back("vmnet3");
  128. result.push_back("vmnet4");
  129. result.push_back("vmnet5");
  130. result.push_back("vmnet6");
  131. result.push_back("vmnet7");
  132. result.push_back("vmnet8");
  133. result.push_back("virbr0");
  134. result.push_back("wlan0");
  135. result.push_back("lo0");
  136. return result;
  137. }
  138. std::string ifname_;
  139. std::string addr_;
  140. std::string key_;
  141. int is_vpn_;
  142. int estimated_speed_;
  143. int type_preference_;
  144. int ip_version_;
  145. };
  146. class InterfacePrioritizer {
  147. public:
  148. InterfacePrioritizer()
  149. : local_addrs_(),
  150. preference_map_(),
  151. sorted_(false) {}
  152. int add(const nr_local_addr *iface) {
  153. LocalAddress addr;
  154. if (!addr.Init(*iface)) {
  155. return R_FAILED;
  156. }
  157. std::pair<std::set<LocalAddress>::iterator, bool> r =
  158. local_addrs_.insert(addr);
  159. if (!r.second) {
  160. return R_ALREADY; // This address is already in the set.
  161. }
  162. sorted_ = false;
  163. return 0;
  164. }
  165. int sort() {
  166. UCHAR tmp_pref = 127;
  167. preference_map_.clear();
  168. for (std::set<LocalAddress>::iterator i = local_addrs_.begin();
  169. i != local_addrs_.end(); ++i) {
  170. if (tmp_pref == 0) {
  171. return R_FAILED;
  172. }
  173. preference_map_.insert(make_pair(i->GetKey(), tmp_pref--));
  174. }
  175. sorted_ = true;
  176. return 0;
  177. }
  178. int getPreference(const char *key, UCHAR *pref) {
  179. if (!sorted_) {
  180. return R_FAILED;
  181. }
  182. std::map<std::string, UCHAR>::iterator i = preference_map_.find(key);
  183. if (i == preference_map_.end()) {
  184. return R_NOT_FOUND;
  185. }
  186. *pref = i->second;
  187. return 0;
  188. }
  189. private:
  190. std::set<LocalAddress> local_addrs_;
  191. std::map<std::string, UCHAR> preference_map_;
  192. bool sorted_;
  193. };
  194. } // anonymous namespace
  195. static int add_interface(void *obj, nr_local_addr *iface) {
  196. InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
  197. return ip->add(iface);
  198. }
  199. static int get_priority(void *obj, const char *key, UCHAR *pref) {
  200. InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
  201. return ip->getPreference(key, pref);
  202. }
  203. static int sort_preference(void *obj) {
  204. InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
  205. return ip->sort();
  206. }
  207. static int destroy(void **objp) {
  208. if (!objp || !*objp) {
  209. return 0;
  210. }
  211. InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(*objp);
  212. *objp = 0;
  213. delete ip;
  214. return 0;
  215. }
  216. static nr_interface_prioritizer_vtbl priorizer_vtbl = {
  217. add_interface,
  218. get_priority,
  219. sort_preference,
  220. destroy
  221. };
  222. namespace mozilla {
  223. nr_interface_prioritizer* CreateInterfacePrioritizer() {
  224. nr_interface_prioritizer *ip;
  225. int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(),
  226. &priorizer_vtbl,
  227. &ip);
  228. if (r != 0) {
  229. return nullptr;
  230. }
  231. return ip;
  232. }
  233. } // namespace mozilla